// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity ^0.8.24; {{IMPORTS}} /// @title {{CONTRACT_NAME}} - Private DEX Order Book /// @notice A dark pool where orders are matched without revealing prices /// @dev Uses FHE for encrypted order matching with async public decryption contract {{CONTRACT_NAME}} is {{INHERITS}} { // ============ Enums ============ enum OrderSide { Buy, Sell } enum OrderStatus { Active, Matching, Filled, Cancelled } // ============ Structs ============ struct Order { address trader; OrderSide side; [[PRICE_TYPE]] encryptedPrice; [[AMOUNT_TYPE]] encryptedAmount; [[AMOUNT_TYPE]] filledAmount; OrderStatus status; uint256 timestamp; } struct PendingMatch { uint256 buyOrderId; uint256 sellOrderId; ebool canMatch; bool isResolved; } // ============ Errors ============ error OrderNotFound(uint256 orderId); error OrderNotActive(uint256 orderId); error NotOrderOwner(); error NoMatchFound(); error InvalidOrder(); error MatchNotPending(); error MatchAlreadyResolved(); error InvalidDecryptionProof(); {{ERRORS}} // ============ Events ============ event OrderPlaced(uint256 indexed orderId, address indexed trader, OrderSide side); event OrderCancelled(uint256 indexed orderId); event MatchRequested(uint256 indexed matchId, uint256 buyOrderId, uint256 sellOrderId); event MatchReadyForReveal(uint256 indexed matchId); event MatchRevealed(uint256 indexed matchId, bool canMatch); event TradeExecuted(uint256 indexed buyOrderId, uint256 indexed sellOrderId, address buyer, address seller); {{EVENTS}} // ============ State Variables ============ mapping(uint256 => Order) private _orders; mapping(uint256 => PendingMatch) private _pendingMatches; uint256 private _orderCount; uint256 private _matchCount; uint256[] private _activeBuyOrders; uint256[] private _activeSellOrders; uint256 public minOrderAmount; {{STATE_VARIABLES}} // ============ Modifiers ============ {{MODIFIERS}} // ============ Constructor ============ constructor(uint256 _minOrderAmount{{CONSTRUCTOR_PARAMS}}) { minOrderAmount = _minOrderAmount; {{CONSTRUCTOR_BODY}} } // ============ External Functions ============ /// @notice Place a buy order with encrypted price and amount function placeBuyOrder( [[EXTERNAL_PRICE_TYPE]] encryptedPrice, bytes calldata priceProof, [[EXTERNAL_AMOUNT_TYPE]] encryptedAmount, bytes calldata amountProof ) external {{ORDER_MODIFIERS}} returns (uint256 orderId) { {{PRE_ORDER}} [[PRICE_TYPE]] price = FHE.fromExternal(encryptedPrice, priceProof); [[AMOUNT_TYPE]] amount = FHE.fromExternal(encryptedAmount, amountProof); orderId = ++_orderCount; _orders[orderId] = Order({ trader: msg.sender, side: OrderSide.Buy, encryptedPrice: price, encryptedAmount: amount, filledAmount: FHE.asEuint64(0), status: OrderStatus.Active, timestamp: block.timestamp }); FHE.allowThis(_orders[orderId].encryptedPrice); FHE.allowThis(_orders[orderId].encryptedAmount); FHE.allowThis(_orders[orderId].filledAmount); FHE.allow(_orders[orderId].encryptedPrice, msg.sender); FHE.allow(_orders[orderId].encryptedAmount, msg.sender); _activeBuyOrders.push(orderId); {{POST_ORDER}} emit OrderPlaced(orderId, msg.sender, OrderSide.Buy); } /// @notice Place a sell order with encrypted price and amount function placeSellOrder( [[EXTERNAL_PRICE_TYPE]] encryptedPrice, bytes calldata priceProof, [[EXTERNAL_AMOUNT_TYPE]] encryptedAmount, bytes calldata amountProof ) external {{ORDER_MODIFIERS}} returns (uint256 orderId) { {{PRE_ORDER}} [[PRICE_TYPE]] price = FHE.fromExternal(encryptedPrice, priceProof); [[AMOUNT_TYPE]] amount = FHE.fromExternal(encryptedAmount, amountProof); orderId = ++_orderCount; _orders[orderId] = Order({ trader: msg.sender, side: OrderSide.Sell, encryptedPrice: price, encryptedAmount: amount, filledAmount: FHE.asEuint64(0), status: OrderStatus.Active, timestamp: block.timestamp }); FHE.allowThis(_orders[orderId].encryptedPrice); FHE.allowThis(_orders[orderId].encryptedAmount); FHE.allowThis(_orders[orderId].filledAmount); FHE.allow(_orders[orderId].encryptedPrice, msg.sender); FHE.allow(_orders[orderId].encryptedAmount, msg.sender); _activeSellOrders.push(orderId); {{POST_ORDER}} emit OrderPlaced(orderId, msg.sender, OrderSide.Sell); } /// @notice Cancel an active order function cancelOrder(uint256 orderId) external { Order storage order = _orders[orderId]; if (order.trader == address(0)) revert OrderNotFound(orderId); if (order.trader != msg.sender) revert NotOrderOwner(); if (order.status != OrderStatus.Active) revert OrderNotActive(orderId); order.status = OrderStatus.Cancelled; emit OrderCancelled(orderId); } /// @notice Request matching check between buy and sell orders /// @dev Step 1: Creates encrypted comparison, marks for decryption function requestMatch(uint256 buyOrderId, uint256 sellOrderId) external returns (uint256 matchId) { {{PRE_MATCH}} Order storage buyOrder = _orders[buyOrderId]; Order storage sellOrder = _orders[sellOrderId]; if (buyOrder.trader == address(0)) revert OrderNotFound(buyOrderId); if (sellOrder.trader == address(0)) revert OrderNotFound(sellOrderId); if (buyOrder.status != OrderStatus.Active) revert OrderNotActive(buyOrderId); if (sellOrder.status != OrderStatus.Active) revert OrderNotActive(sellOrderId); if (buyOrder.side != OrderSide.Buy) revert InvalidOrder(); if (sellOrder.side != OrderSide.Sell) revert InvalidOrder(); // Encrypted comparison: buyPrice >= sellPrice ebool canMatch = FHE.ge(buyOrder.encryptedPrice, sellOrder.encryptedPrice); matchId = ++_matchCount; _pendingMatches[matchId] = PendingMatch({ buyOrderId: buyOrderId, sellOrderId: sellOrderId, canMatch: canMatch, isResolved: false }); // Mark orders as pending match buyOrder.status = OrderStatus.Matching; sellOrder.status = OrderStatus.Matching; FHE.allowThis(canMatch); // Mark for public decryption FHE.makePubliclyDecryptable(canMatch); emit MatchRequested(matchId, buyOrderId, sellOrderId); emit MatchReadyForReveal(matchId); {{POST_MATCH}} } /// @notice Get the encrypted match result handle for off-chain decryption function getMatchHandle(uint256 matchId) external view returns (ebool) { PendingMatch storage pm = _pendingMatches[matchId]; if (pm.buyOrderId == 0) revert MatchNotPending(); return pm.canMatch; } /// @notice Finalize match with decryption proof /// @dev Step 3: Verify decryption and execute trade if matched function finalizeMatch( uint256 matchId, bool canMatchResult, bytes calldata decryptionProof ) external { PendingMatch storage pm = _pendingMatches[matchId]; if (pm.buyOrderId == 0) revert MatchNotPending(); if (pm.isResolved) revert MatchAlreadyResolved(); // Verify decryption proof bytes32[] memory cts = new bytes32[](1); cts[0] = ebool.unwrap(pm.canMatch); bytes memory cleartexts = abi.encode(canMatchResult); // This reverts if proof is invalid FHE.checkSignatures(cts, cleartexts, decryptionProof); pm.isResolved = true; Order storage buyOrder = _orders[pm.buyOrderId]; Order storage sellOrder = _orders[pm.sellOrderId]; if (canMatchResult) { // Execute trade _executeMatch(pm.buyOrderId, pm.sellOrderId); emit MatchRevealed(matchId, true); } else { // No match - revert orders to active buyOrder.status = OrderStatus.Active; sellOrder.status = OrderStatus.Active; emit MatchRevealed(matchId, false); } } {{EXTERNAL_FUNCTIONS}} // ============ View Functions ============ function getOrderCount() external view returns (uint256) { return _orderCount; } function getMatchCount() external view returns (uint256) { return _matchCount; } function getActiveBuyOrderCount() external view returns (uint256) { return _activeBuyOrders.length; } function getActiveSellOrderCount() external view returns (uint256) { return _activeSellOrders.length; } function getOrderInfo(uint256 orderId) external view returns ( address trader, OrderSide side, OrderStatus status, uint256 timestamp ) { Order storage order = _orders[orderId]; return (order.trader, order.side, order.status, order.timestamp); } function getMatchInfo(uint256 matchId) external view returns ( uint256 buyOrderId, uint256 sellOrderId, bool isResolved ) { PendingMatch storage pm = _pendingMatches[matchId]; return (pm.buyOrderId, pm.sellOrderId, pm.isResolved); } function getOrderPrice(uint256 orderId) external view returns ([[PRICE_TYPE]]) { return _orders[orderId].encryptedPrice; } function getOrderAmount(uint256 orderId) external view returns ([[AMOUNT_TYPE]]) { return _orders[orderId].encryptedAmount; } {{VIEW_FUNCTIONS}} // ============ Internal Functions ============ function _executeMatch(uint256 buyOrderId, uint256 sellOrderId) internal { Order storage buyOrder = _orders[buyOrderId]; Order storage sellOrder = _orders[sellOrderId]; // Calculate trade amount: min(buyAmount - buyFilled, sellAmount - sellFilled) [[AMOUNT_TYPE]] buyRemaining = FHE.sub(buyOrder.encryptedAmount, buyOrder.filledAmount); [[AMOUNT_TYPE]] sellRemaining = FHE.sub(sellOrder.encryptedAmount, sellOrder.filledAmount); [[AMOUNT_TYPE]] tradeAmount = FHE.min(buyRemaining, sellRemaining); // Update filled amounts buyOrder.filledAmount = FHE.add(buyOrder.filledAmount, tradeAmount); sellOrder.filledAmount = FHE.add(sellOrder.filledAmount, tradeAmount); FHE.allowThis(buyOrder.filledAmount); FHE.allowThis(sellOrder.filledAmount); FHE.allowThis(tradeAmount); // Mark as filled (simplified - full implementation would check remaining) buyOrder.status = OrderStatus.Filled; sellOrder.status = OrderStatus.Filled; emit TradeExecuted(buyOrderId, sellOrderId, buyOrder.trader, sellOrder.trader); } {{INTERNAL_FUNCTIONS}} }