DailyAuction
Multi-platform daily inference auction. T+1, all-or-nothing, uniform-clearing on KAI.
contracts/src/auction/DailyAuction.sol
Inherits
Ownable2Step, ReentrancyGuard
Constants
| Name | Value | Meaning |
|---|---|---|
MAX_BIDS_PER_DAY | 1000 | Per (platform, day) |
MIN_BID_DD | 1e18 | ≥ 1 DD per bid |
MIN_KAI_BID | 1e16 | ≥ 0.01 KAI per bid (anti-grief) |
GRACE_PERIOD | 7 days | Settle escape hatch |
MAX_KAI_PER_DD | 1e18 | Hard ceiling on the leftover KAI/DD fee rate (1 KAI per 1 DD) |
DAY | 1 days |
Immutables
| Name | Type |
|---|---|
dd | IERC20 (Dollar Day) |
kai | IERC20 (Kairence) |
Mutable state
| Name | Type |
|---|---|
backend | Authorized for markKeyIssued |
isRegistered[platform] | bool mapping |
kaiPerDD[platform] | uint128 — fee rate for buyLeftover (KAI wei per 1 DD; default 0 = DD-only) |
_bids[platform][day] | Array of Bid struct |
dayState[platform][day] | DayState per day |
Bid struct
struct Bid {
address bidder;
uint128 ddAmount;
uint128 kaiBid;
uint64 bidTime;
uint64 keyIssuedAt;
bool winner;
bool refunded;
}DayState struct
struct DayState {
bool settled;
uint16 winnerCount;
uint128 clearingKaiBid;
uint128 totalDDWon;
uint128 leftoverDDSold;
uint128 dailyCapSnapshot;
}Admin functions
function registerPlatform(address platform) external onlyOwner;
function deregisterPlatform(address platform) external onlyOwner;
function setBackend(address backend_) external onlyOwner;
function setKaiPerDD(address platform, uint128 rate) external onlyOwner;registerPlatform does a try / catch IPlatform(platform).dailyCap() to catch typo deploys (PlatformValidationFailed).
setKaiPerDD requires the platform to be registered and rate ≤ MAX_KAI_PER_DD. No timelock — operational fee parameter, consistent with setBackend.
Bidding
function bid(address platform, uint128 ddAmount, uint128 kaiBid)
external nonReentrant
returns (uint64 computeDay, uint256 bidIndex);platformmust be registered.ddAmount ≥ MIN_BID_DD,kaiBid ≥ MIN_KAI_BID.- DD and KAI pulled from
msg.senderinto auction escrow. computeDay = currentDay + 1.bidIndexis the position in_bids[platform][computeDay].
Settlement
function settle(address platform, uint64 computeDay, uint256[] calldata orderedIdx)
external nonReentrant;Verifies orderedIdx is a valid permutation of [0, n) AND that bids in that order are non-decreasing in priority (kaiBid DESC, bidTime ASC tie-break). Reverts on any mismatch:
OrderingLengthMismatch | IndexOutOfRange | DuplicateIndex | NotSortedThen runs all-or-nothing fill (oversized bids skipped, see Daily auction), burns winning DD + clearing × winners KAI, sets winner flags.
Anyone can call. Off-chain caller computes orderedIdx.
Refunds & escape
function claimRefund(address platform, uint64 computeDay, uint256 bidIndex)
external nonReentrant;- If day is settled and
b.winner == false: full DD + full KAI refund. - If day is settled and
b.winner == true: refund(kaiBid - clearing)KAI only (DD already burned). - If day is not settled AND
block.timestamp ≥ computeDay × DAY + GRACE_PERIOD: escape hatch, full DD + KAI refund.
AlreadyRefunded reverts on second call.
Leftover
function buyLeftover(address platform, uint64 computeDay, uint128 ddAmount)
external nonReentrant
returns (uint256 bidIndex);Buys unsold capacity post-settle (during the inference day only):
ds.settledrequired.block.timestamp < (computeDay + 1) × DAY.totalDDWon + leftoverDDSold + ddAmount ≤ dailyCapSnapshot.
DD pulled in and burned immediately. KAI fee kaiBurn = ddAmount × kaiPerDD[platform] / 1e18 is also pulled and burned when kaiPerDD > 0. Records a "winner" bid with kaiBid = kaiBurn and refunded = true (no refund needed, already paid).
Key flow
function requestKey(address platform, uint64 computeDay, uint256 bidIndex) external;
function markKeyIssued(address platform, uint64 computeDay, uint256 bidIndex) external;requestKey (winner-only) emits KeyRequested — backend picks it up. markKeyIssued (backend-only) sets bid.keyIssuedAt = now.
Views
function bidCount(address platform, uint64 computeDay) external view returns (uint256);
function bidInfo(address platform, uint64 computeDay, uint256 bidIndex) external view returns (Bid memory);
function currentDay() external view returns (uint64);Events
PlatformRegistered, PlatformDeregistered, BackendSet, KaiPerDDSet,
BidSubmitted, Settled,
BidRefunded, LeftoverBought,
KeyRequested, KeyIssuedLeftoverBought includes the burned KAI amount as the last field.
Errors
ZeroAddress, ZeroAmount, UnknownPlatform, PlatformAlreadyRegistered,
BidTooSmall, KaiBidTooSmall, DayFull,
AlreadySettled, NotSettled, TooEarly, TooLate,
UnknownBid, NotBidder, NotBackend,
AlreadyRefunded, NotWinner, KeyAlreadyIssued,
InsufficientLeftover, PlatformValidationFailed,
OrderingLengthMismatch, IndexOutOfRange, DuplicateIndex, NotSorted,
KaiPerDDTooHighGas
At cap 1000 bids:
settleworst case (1000 bids, all sorted correctly): ~4M gas (verify O(n) + walk O(n)).bid: ~120k gas (twotransferFrom+ one storage push).claimRefund: ~60k gas.buyLeftover: ~80k gas (DD transferFrom + burn + push), +~50k ifkaiPerDD > 0(KAI transferFrom + burn).
Settle limit comfortable on Base (block gas ~150M).