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
| Parameter | Constraint | Why |
|---|---|---|
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 |
platform | Must be registered | DailyAuction.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 bid | DD requested | Cumulative | Outcome |
|---|---|---|---|
| 10 KAI | 6 | 6 | win |
| 5 KAI | 10 | (10 > 8) skipped | LOSE (oversized) |
| 3 KAI | 2 | 8 | win |
| 2 KAI | 1 | (8+1 > 8) skipped | LOSE |
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:
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:
auction.buyLeftover(platform, day, ddAmount);
// pulls: ddAmount of DD → burned
// + ddAmount × kaiPerDD/1e18 of KAI → burnedThe 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):
ddAmount | KAI burned |
|---|---|
| 1 DD | 0.05 KAI |
| 10 DD | 0.5 KAI |
| 100 DD | 5 KAI |
Rules:
ddAmountis burned immediately.kaiBurn = ddAmount × kaiPerDD[platform] / 1e18KAI is pulled and burned. IfkaiPerDD = 0(default at deploy), no KAI is required.- The bid is recorded as a winner (so
requestKeyworks) withrefunded = 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.
auction.setKaiPerDD(platform, 5e16); // 0.05 KAI per 1 DDThe 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
| Status | DD refunded | KAI refunded |
|---|---|---|
| Loser (not winner) | Full | Full |
| Winner | None (burned) | kaiBid - clearing |
| Escape hatch (settle never called past 7d) | Full | Full |
buyLeftover participant | N/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 APIBackend 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:
- Implement
IPlatform { dailyCap() returns (uint256) }. - Get registered by owner via
auction.registerPlatform(platform).
Bidders choose which platform's inference they're bidding for in each call.