// Auction — Phase D of silent-iron-keystone v4. // // Supports both Dutch (price decay; first-to-accept wins) and English // (sealed-bid; highest revealed wins) modes. Mode is stored at // auction creation; mode-specific entrypoints separate the flows. // // Privacy: bidder identity is one-time stealth (CryptoNote default). // English auction bids are commit-reveal. Dutch auction acceptance // uses a 3-block commit-reveal window to prevent mempool front-running // (see plan §AUDIT F-04). dark contract Auction { // mode: 1=Dutch, 2=English public mapping(bytes => uint8) auctionMode; public mapping(bytes => bytes) auctionTokenId; public mapping(bytes => uint64) auctionTotalSupply; public mapping(bytes => uint64) auctionStartBlock; public mapping(bytes => uint64) auctionEndBlock; public mapping(bytes => uint64) auctionStartPrice; // Dutch only public mapping(bytes => uint64) auctionEndPrice; // Dutch only public mapping(bytes => bool) auctionSettled; public mapping(bytes => bool) acceptedNullifiers; public mapping(bytes => bytes) acceptedCommit; // accept_commit_id → commit blob public mapping(bytes => uint64) acceptedCommitBlock; @direct entry createDutch(bytes auction_id, bytes tokenId, uint64 totalSupply, uint64 startPrice, uint64 endPrice, uint64 startBlock, uint64 endBlock) { require(auctionMode[auction_id] == 0, "AUC_DUPLICATE"); require(totalSupply > 0, "AUC_ZERO_SUPPLY"); require(startPrice > endPrice, "AUC_DUTCH_INVERTED"); require(endBlock > startBlock, "AUC_END_BEFORE_START"); require(startBlock >= block.number, "AUC_START_IN_PAST"); auctionMode[auction_id] = 1; auctionTokenId[auction_id] = tokenId; auctionTotalSupply[auction_id] = totalSupply; auctionStartBlock[auction_id] = startBlock; auctionEndBlock[auction_id] = endBlock; auctionStartPrice[auction_id] = startPrice; auctionEndPrice[auction_id] = endPrice; auctionSettled[auction_id] = false; } @direct entry createEnglish(bytes auction_id, bytes tokenId, uint64 totalSupply, uint64 startBlock, uint64 endBlock) { require(auctionMode[auction_id] == 0, "AUC_DUPLICATE"); require(totalSupply > 0, "AUC_ZERO_SUPPLY"); require(endBlock > startBlock, "AUC_END_BEFORE_START"); require(startBlock >= block.number, "AUC_START_IN_PAST"); auctionMode[auction_id] = 2; auctionTokenId[auction_id] = tokenId; auctionTotalSupply[auction_id] = totalSupply; auctionStartBlock[auction_id] = startBlock; auctionEndBlock[auction_id] = endBlock; auctionSettled[auction_id] = false; } // Dutch — commit-then-reveal within 3 blocks. @direct entry acceptCommit(bytes auction_id, bytes accept_commit_id, bytes commitment) { require(auctionMode[auction_id] == 1, "AUC_NOT_DUTCH"); require(auctionStartBlock[auction_id] <= block.number, "AUC_NOT_STARTED"); require(block.number < auctionEndBlock[auction_id], "AUC_ENDED"); require(!auctionSettled[auction_id], "AUC_SETTLED"); require(acceptedCommitBlock[accept_commit_id] == 0, "AUC_COMMIT_DUPLICATE"); acceptedCommit[accept_commit_id] = commitment; acceptedCommitBlock[accept_commit_id] = block.number; } @direct entry acceptReveal(bytes auction_id, bytes accept_commit_id, uint64 quantity, bytes nullifier) { require(auctionMode[auction_id] == 1, "AUC_NOT_DUTCH"); require(acceptedCommitBlock[accept_commit_id] != 0, "AUC_COMMIT_NOT_FOUND"); require(block.number <= acceptedCommitBlock[accept_commit_id] + 3, "AUC_REVEAL_STALE"); require(!acceptedNullifiers[nullifier], "AUC_REPLAY"); require(quantity > 0, "AUC_ZERO_QUANTITY"); require(quantity <= auctionTotalSupply[auction_id], "AUC_OVER_SUPPLY"); acceptedNullifiers[nullifier] = true; auctionTotalSupply[auction_id] = auctionTotalSupply[auction_id] - quantity; syscall(TOKEN_TRANSFER_EMIT_V1, ctx.txHash); } // English — sealed bid then reveal after end block. @direct entry submitBid(bytes auction_id, bytes bid_id, bytes commitment) { require(auctionMode[auction_id] == 2, "AUC_NOT_ENGLISH"); require(block.number < auctionEndBlock[auction_id], "AUC_BID_PAST_END"); require(acceptedCommit[bid_id] == "", "AUC_BID_DUPLICATE"); acceptedCommit[bid_id] = commitment; acceptedCommitBlock[bid_id] = block.number; } // Operator settles English auction. Caller proves they hold the // winning bid's opening; contract verifies nullifier freshness. @direct entry settleEnglish(bytes auction_id, bytes bid_id, bytes nullifier, uint64 quantity) { require(auctionMode[auction_id] == 2, "AUC_NOT_ENGLISH"); require(block.number >= auctionEndBlock[auction_id], "AUC_BEFORE_END"); require(!auctionSettled[auction_id], "AUC_ALREADY_SETTLED"); require(!acceptedNullifiers[nullifier], "AUC_REPLAY"); require(quantity > 0, "AUC_ZERO_QUANTITY"); require(quantity <= auctionTotalSupply[auction_id], "AUC_OVER_SUPPLY"); auctionSettled[auction_id] = true; acceptedNullifiers[nullifier] = true; syscall(TOKEN_TRANSFER_EMIT_V1, ctx.txHash); } }