5.2. FhenixFairMarket.sol (Core Logic)
FhenixFairMarket.sol serves as the primary implementation contract for the protocol’s UUPS proxy architecture. It encapsulates the entire auction lifecycle, from cryptographic escrow locking and sealed-bid submission to asynchronous CoFHE resolution and pull-based settlement. By strictly separating executable logic from persistent storage, this contract ensures safe, zero-downtime upgrades while enforcing mathematical guarantees around bid solvency, state transitions, and emergency fund recovery.
All cryptographic operations are routed through ICofheAdapter, and all state mutations are guarded by role-based modifiers and reentrancy protections, making it the foundational trust anchor of the Fhenix-FairMarket ecosystem.
Core Design Principles
| Principle | Technical Implementation |
|---|---|
| UUPS Logic/Storage Separation | Inherits UUPSUpgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable. State lives in proxy; logic executes via delegatecall. |
| State-Guarded Transitions | Every function validates AuctionState via custom modifiers (onlyActive, onlyResolving, notFinalized). Unauthorized jumps revert instantly. |
| O(1) Encrypted Storage | Bids store only a bytes32 keccak256 hash of the InEuint32 ciphertext. No FHE objects or plaintext values persist on-chain. |
| Adapter-Isolated Cryptography | Zero direct imports of @fhenixprotocol/cofhe-contracts. All FHE calls route through ICofheAdapter to prevent SDK upgrade breakage. |
| Pull-Over-Push Settlement | Refunds use individual claimRefund() paths with hasWithdrawn mapping. State updates execute before external value transfers. |
| Dynamic Emergency Fallback | triggerFallbackVoid() activates based on Moving Time Average × 1.5 of block timestamps, guaranteeing 100% liquidity recovery during sequencer/AVS outages. |
State Machine & Lifecycle Architecture
The contract enforces a strict 6-state lifecycle to ensure deterministic behavior across asynchronous resolution and emergency recovery.
| State | Description | Permitted Actions |
|---|---|---|
CREATED | Auction initialized, NFT pending lock | lockEscrow(), createAuction() |
ACTIVE | Bidding open, escrow accumulating | placeBid(), cancelAuction(), triggerFinalize() |
RESOLVING | Bids closed, off-chain CoFHE processing | submitResolution(), triggerFallbackVoid() |
FINALIZED | Winner declared, refunds open | claimRefund() (pull), NFT transfer to winner |
CANCELLED | Seller aborted pre-closure | claimRefund() (pro-rata from SlashedPot) |
VOIDED | Emergency timeout triggered | claimRefund() (100% recovery), FHE engine disabled |
️ Critical Function Reference
1. Encrypted Bid Submission (placeBid)
Executes the cryptographic solvency gate and registers the bid hash with O(1) gas complexity.
function placeBid(uint256 auctionId, InEuint32 calldata encryptedBid) external nonReentrant onlyActive(auctionId) {
Auction storage auction = auctions[auctionId];
require(block.timestamp < auction.endTime, "Auction expired");
// Adapter-Isolated Solvency Check (Zero plaintext leak)
ICofheAdapter(cofheAdapter).verifySolvency(encryptedBid, escrowBalances[msg.sender]);
// O(1) Storage: Only ciphertext hash persists
bytes32 bidHash = keccak256(abi.encode(encryptedBid));
auction.ciphertextHashes.push(bidHash);
emit BidPlaced(auctionId, msg.sender, bidHash);
}2. Asynchronous Finalization (triggerFinalize)
Locks the auction state and emits the DecryptionRequested event to trigger off-chain processing.
function triggerFinalize(uint256 auctionId) external nonReentrant {
require(auctions[auctionId].state == AuctionState.ACTIVE, "Not active");
require(block.timestamp >= auctions[auctionId].endTime, "Not ended yet");
// Atomic state transition
auctions[auctionId].state = AuctionState.RESOLVING;
// Dispatch to off-chain CoFHE layer
emit DecryptionRequested(auctionId, auctions[auctionId].ciphertextHashes);
// Keeper incentive (0.2%) distributed via SettlementEngine
_distributeKeeperBounty(auctionId, msg.sender);
}3. AVS-Verified Resolution (submitResolution)
Accepts off-chain results only after cryptographic proof verification.
function submitResolution(uint256 auctionId, bytes32 winnerCiphertext, bytes calldata avsProof) external nonReentrant {
require(auctions[auctionId].state == AuctionState.RESOLVING, "Not resolving");
// EigenLayer AVS Fraud Proof verification
ISettlementEngine(settlementEngine).verifyResolution(auctionId, winnerCiphertext, avsProof);
auctions[auctionId].state = AuctionState.FINALIZED;
auctions[auctionId].winnerCiphertext = winnerCiphertext;
emit AuctionFinalized(auctionId, winnerCiphertext);
}4. Pull-Based Refund (claimRefund)
State-first mutation prevents reentrancy and Out-of-Gas cascades.
function claimRefund(uint256 _auctionId) external nonReentrant {
AuctionState state = auctions[_auctionId].state;
require(state == FINALIZED || state == CANCELLED || state == VOIDED, "Invalid state");
require(!auctions[_auctionId].hasWithdrawn[msg.sender], "Already withdrawn");
uint256 amount = escrowBalances[msg.sender];
require(amount > 0, "No balance");
// ️ CRITICAL: State mutated BEFORE external transfer
escrowBalances[msg.sender] = 0;
auctions[_auctionId].hasWithdrawn[msg.sender] = true;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Refund failed");
emit RefundClaimed(_auctionId, msg.sender, amount);
}5. Dynamic Emergency Void (triggerFallbackVoid)
Permissionless recovery mechanism activated during infrastructure degradation.
function triggerFallbackVoid(uint256 auctionId) external {
Auction storage auction = auctions[auctionId];
require(auction.state == AuctionState.RESOLVING, "Not resolving");
// Network-volatility-aware threshold
uint256 timeout = _getResolutionTimeout();
require(block.timestamp > auction.endTime + timeout, "Timeout not exceeded");
auction.state = AuctionState.VOIDED;
auction.fheEngineActive = false; // Disables further decryption
auction.lastBlockTimestamp = block.timestamp;
emit AuctionVoided(auctionId, block.timestamp, timeout);
}
function _getResolutionTimeout() internal view returns (uint256) {
uint256 lastTime = lastBlockTimestamp > 0 ? lastBlockTimestamp : block.timestamp - 12;
return (block.timestamp - lastTime) * 1.5; // Moving Time Average × 1.5
}Storage Layout & Proxy Compatibility
To maintain UUPS upgrade safety, the contract uses OpenZeppelin storage slots and avoids layout collisions:
struct Auction {
address nftContract;
uint256 tokenId;
address payable seller;
uint256 endTime;
uint256 sellerDeposit;
AuctionState state;
bool isVickrey;
bool fheEngineActive;
bytes32 winnerCiphertext;
uint256 lastBlockTimestamp; // For DynamicTimeout calculation
mapping(address => uint256) public escrowBalances;
mapping(address => bool) public hasWithdrawn;
bytes32[] public ciphertextHashes;
}
// Proxy-reserved slots (EIP-1967)
// _owner, _implementation, _proxiableUUID are strictly isolated️ Security & Economic Guarantees
- Reentrancy Immunity: All external interactions (
call{value: amount}) are preceded by state mutations (hasWithdrawn = true,escrowBalances = 0).ReentrancyGuardUpgradeableprovides a secondary defense layer. - Zero Plaintext Leakage:
BidPlaced,DecryptionRequested, andAuctionFinalizedevents emit onlybytes32hashes and state identifiers. CI lint rules enforce strict compliance. - Solvency Enforcement:
FHE.lteexecutes homomorphically againstescrowBalances. Invalid bids revert instantly without consuming coprocessor resources or gas beyond the base transaction. - Graceful Degradation: If
triggerFinalize()fails due to network congestion,triggerFallbackVoid()provides a deterministic exit path. Capital recovery is prioritized over cryptographic settlement (Funds Over Privacy). - Upgrade Safety:
_authorizeUpgrade()restricts execution to the governance multisig. All upgrades pass through a48h Timelock, allowing community review before logic swaps.
Audit Gate Compliance (P0)
Progression from Phase 1/2 is strictly blocked until all core logic P0 items pass:
- [] Contract inherits
UUPSUpgradeableand blocksupgradeToAndCallfor non-owners. - [] Zero direct
importof@fhenixprotocol/cofhe-contractsin core logic; all calls route throughICofheAdapter. - []
FHE.ltesolvency gate reverts instantly ifencryptedBid > escrowBalances. - []
claimRefund()updateshasWithdrawnbeforecall{value: amount}. Zerofor/whileloops in payout logic. - []
triggerFallbackVoid()activates only afterMoving Time Average × 1.5threshold, disabling FHE engine and unlocking 100% liquidity. - [] All 6 state transitions are guarded by modifiers; unauthorized jumps revert with precise error strings.
- []
initialize()executes exactly once; storage layout matches EIP-1967 proxy standards without collisions. - [] No plaintext bid values or exact settlement prices emitted in Events.
Next Steps
- Proceed to 5.3. Keeper Network (Automation) to understand off-chain
auctionMonitor,cofheDispatcher, andavsSubmitterorchestration.- Review 5.1. CofheAdapter.sol (FHE Isolation) for SDK abstraction layers and cryptographic wrappers.
- Explore Security Model → Pull Refund Pattern for detailed reentrancy mitigation and gas-safe settlement mechanics.