Skip to content

DailyAuction

Multi-platform daily inference auction. T+1, all-or-nothing, uniform-clearing on KAI.

contracts/src/auction/DailyAuction.sol

Inherits

Ownable2Step, ReentrancyGuard

Constants

NameValueMeaning
MAX_BIDS_PER_DAY1000Per (platform, day)
MIN_BID_DD1e18≥ 1 DD per bid
MIN_KAI_BID1e16≥ 0.01 KAI per bid (anti-grief)
GRACE_PERIOD7 daysSettle escape hatch
MAX_KAI_PER_DD1e18Hard ceiling on the leftover KAI/DD fee rate (1 KAI per 1 DD)
DAY1 days

Immutables

NameType
ddIERC20 (Dollar Day)
kaiIERC20 (Kairence)

Mutable state

NameType
backendAuthorized 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

solidity
struct Bid {
    address bidder;
    uint128 ddAmount;
    uint128 kaiBid;
    uint64  bidTime;
    uint64  keyIssuedAt;
    bool    winner;
    bool    refunded;
}

DayState struct

solidity
struct DayState {
    bool    settled;
    uint16  winnerCount;
    uint128 clearingKaiBid;
    uint128 totalDDWon;
    uint128 leftoverDDSold;
    uint128 dailyCapSnapshot;
}

Admin functions

solidity
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

solidity
function bid(address platform, uint128 ddAmount, uint128 kaiBid)
    external nonReentrant
    returns (uint64 computeDay, uint256 bidIndex);
  • platform must be registered.
  • ddAmount ≥ MIN_BID_DD, kaiBid ≥ MIN_KAI_BID.
  • DD and KAI pulled from msg.sender into auction escrow.
  • computeDay = currentDay + 1.
  • bidIndex is the position in _bids[platform][computeDay].

Settlement

solidity
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 | NotSorted

Then 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

solidity
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

solidity
function buyLeftover(address platform, uint64 computeDay, uint128 ddAmount)
    external nonReentrant
    returns (uint256 bidIndex);

Buys unsold capacity post-settle (during the inference day only):

  • ds.settled required.
  • 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

solidity
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

solidity
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, KeyIssued

LeftoverBought 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,
KaiPerDDTooHigh

Gas

At cap 1000 bids:

  • settle worst case (1000 bids, all sorted correctly): ~4M gas (verify O(n) + walk O(n)).
  • bid: ~120k gas (two transferFrom + one storage push).
  • claimRefund: ~60k gas.
  • buyLeftover: ~80k gas (DD transferFrom + burn + push), +~50k if kaiPerDD > 0 (KAI transferFrom + burn).

Settle limit comfortable on Base (block gas ~150M).

Released under the MIT License.