Skip to content

Daily inference auction

Every day, the DailyAuction sells the next UTC day's total inference capacity. The auction is T+1, all-or-nothing, uniform-clearing.

Lifecycle, day by day

Day D, 00:00 UTC
  Auction for day D+1 opens.
  Bidders call bid(platform, ddAmount, kaiBid) anytime today.

Day D+1, 00:00 UTC
  Auction for day D+1 closes (no more bids accepted).
  Anyone calls settle(platform, D+1, orderedIdx):
    • Verifies orderedIdx is a valid sort (kaiBid DESC, bidTime ASC tie-break)
    • All-or-nothing fill — bids that don't fit cap are SKIPPED
    • Burns winning DD at face, burns clearing KAI × winners
    • Sets winner flags

Day D+1, any time
  Winners call requestKey(platform, D+1, bidIndex)
  Backend issues Venice API key via REST, calls markKeyIssued
  Winner pulls the actual key string from backend's REST endpoint
  Winner uses the key on Venice for inference until 23:59:59 UTC

  Anyone can call buyLeftover(platform, D+1, ddAmount) to scoop unsold
  capacity (DD + KAI fee = ddAmount × kaiPerDD).

Day D+2 and beyond
  Losers call claimRefund(platform, D+1, bidIndex) — full DD + KAI back.
  Winners with overbid KAI also call claimRefund — refunds (kaiBid - clearing).

Bid constraints

ParameterConstraintWhy
ddAmount≥ 1 DD (MIN_BID_DD = 1e18)Anti-dust
kaiBid≥ 0.01 KAI (MIN_KAI_BID = 1e16)Anti-grief (closes 0-KAI spam attack)
Bids per day≤ 1000 (MAX_BIDS_PER_DAY)Bounded settle gas
platformMust be registeredDailyAuction.isRegistered[platform]

Both DD and KAI are pulled from your wallet into the auction escrow at bid time. You must have approved both tokens for the auction.

All-or-nothing fill

If your bid's ddAmount doesn't fit the remaining capacity at the moment the settlement considers your bid, your bid is skipped entirely — not partially filled. The settlement then tries the next-best bid which may fit.

Example with cap = 8 DD:

Sorted bidDD requestedCumulativeOutcome
10 KAI66win
5 KAI10(10 > 8) skippedLOSE (oversized)
3 KAI28win
2 KAI1(8+1 > 8) skippedLOSE

Result: 2 winners, ddBurn = 6 + 2 = 8, clearing = 3 KAI.

This protects users from getting a "half-useful" key. Either you get the full ddAmount you asked for, or you get a full refund.

It also closes a settle-blocking grief: with break (the earlier design), a single oversized top bid would have stopped allocation entirely. With continue (current design), the oversized bid is just skipped.

Uniform clearing

Winners all pay the lowest winning KAI bid, not their own. If you bid 10 KAI and clearing came in at 3 KAI:

  • Your KAI overbid (10 - 3 = 7 KAI) is refundable via claimRefund.
  • The 3 KAI you actually paid is burned at settle.

DD is not uniform-cleared — each winner burns their own ddAmount exactly (since DD is per-bid inference amount, not a price).

Who calls settle

Anyone. Most often it's the backend cron at 00:00:30 UTC. But if the backend is down, any bidder (or any random party) can call:

ts
const order = await buildOrderedIdx(platform, day);
await auction.write.settle([platform, day, order]);

orderedIdx is computed off-chain (sort bids by kaiBid DESC, bidTime ASC) and verified on-chain. If the order is wrong, the call reverts and the caller's gas is lost — incentive to submit a correct ordering.

Escape hatch (GRACE_PERIOD)

If settle is never called for a day and GRACE_PERIOD = 7 days elapses past 00:00 UTC of the inference day, every bidder can call claimRefund and pull a full refund of both DD and KAI.

This guarantees no funds can be permanently stranded by a broken settle — worst case, bidders wait 7 days and get everything back.

buyLeftover

After settle, if the auction filled less than the day's cap, anyone can scoop the unsold capacity. The cost is the DD itself plus a proportional KAI fee set per-platform by the owner:

solidity
auction.buyLeftover(platform, day, ddAmount);
//   pulls:  ddAmount of DD          → burned
//          + ddAmount × kaiPerDD/1e18 of KAI → burned

The fee structure is by design: KAI is always tied to how much DD you spend — the more inference you take, the more KAI you burn. Concrete numbers (with kaiPerDD = 0.05 KAI/DD):

ddAmountKAI burned
1 DD0.05 KAI
10 DD0.5 KAI
100 DD5 KAI

Rules:

  • ddAmount is burned immediately.
  • kaiBurn = ddAmount × kaiPerDD[platform] / 1e18 KAI is pulled and burned. If kaiPerDD = 0 (default at deploy), no KAI is required.
  • The bid is recorded as a winner (so requestKey works) with refunded = true.
  • Available only during the inference day itself (block.timestamp < (day+1) × 1 day).

Setting kaiPerDD

kaiPerDD[platform] is an owner-settable rate (no timelock — it's an operational fee parameter, not a role rotation). Capped at MAX_KAI_PER_DD = 1 KAI per 1 DD so a misbehaving owner cannot price leftover purchases out of reach.

solidity
auction.setKaiPerDD(platform, 5e16); // 0.05 KAI per 1 DD

The bid auction itself (bid + settle) is unaffected — that path keeps the uniform-clearing KAI premium model. The leftover path uses a fixed fee because there's no competitive bidding window.

Refund matrix

StatusDD refundedKAI refunded
Loser (not winner)FullFull
WinnerNone (burned)kaiBid - clearing
Escape hatch (settle never called past 7d)FullFull
buyLeftover participantN/A (DD + KAI fee already burned)N/A

Why deflationary

Every successful auction burns DD (inference consumed) and KAI (scarcity rent). KAI supply only decreases — there is no continuous emission. This gives KAI a clean scarcity model tied to inference demand growth.

Key issuance flow (backend)

1. Winner calls requestKey(platform, day, bidIndex)
   → emits KeyRequested
2. Backend listens for KeyRequested events
3. Backend calls Venice REST POST /api_keys with:
     consumptionLimit.diem = bid.ddAmount
     expiresAt = end of day UTC
4. Backend stores the returned key string in its own DB
5. Backend calls auction.markKeyIssued(platform, day, bidIndex)
   → emits KeyIssued, sets bid.keyIssuedAt = now
6. Winner pulls the key from backend REST: GET /api/keys/get?bidId=...
7. Winner uses the key with Venice's inference API

Backend authority is scoped — the backend EOA can only call markKeyIssued. It cannot affect bids, refunds, or settlement. A compromised backend's blast radius is: it can fail to issue Venice keys for winners (operational impact: winners burn DD/KAI without getting a key). Mitigated by monitoring + redundant backend.

Multi-platform

DailyAuction is parameterized by platform. The same auction contract will host future platforms (TAO, AKT) when they integrate. Each platform needs:

  1. Implement IPlatform { dailyCap() returns (uint256) }.
  2. Get registered by owner via auction.registerPlatform(platform).

Bidders choose which platform's inference they're bidding for in each call.

Released under the MIT License.