7.4. Confidence Dashboard
The Confidence Dashboard is the user-facing manifestation of Fhenix-FairMarket’s core privacy philosophy: “Trust in proof, not in intermediaries.” Traditional auction interfaces expose bid values, participant addresses, and ranking data—information that, in a sealed-bid context, fundamentally undermines cryptographic guarantees and enables strategic manipulation.
Version 2.0 resolves this through Progressive Disclosure: a design pattern that displays only safe, aggregated, or state-gated metrics until cryptographic settlement is complete. Users gain confidence through transparency about process—not through exposure of sensitive data—while the protocol maintains mathematical privacy end-to-end.
Core Design Principles
| Principle | Technical Implementation |
|---|---|
| Zero Plaintext Display | No numeric bid values, winner addresses, or decrypted data are shown before FINALIZED or VOIDED state. Enforced via component-level guards and CI lint rules. |
| State-Gated Visibility | Metrics dynamically adapt to AuctionState: ACTIVE shows encryption status; RESOLVING shows processing progress; FINALIZED reveals outcome. |
| Terminology Abstraction | Technical terms (FHE, CoFHE, AVS) are replaced with user-centric language ( Protected Bid, ️ Sealed Vault, Network Verified). |
| Real-Time WebSocket Sync | Dashboard subscribes to contract events (BidPlaced, AuctionFinalized) via wagmi/viem for sub-2-second latency updates without polling. |
| Accessibility Compliance | All metrics include ARIA labels, color-blind safe palettes, and screen-reader optimized text alternatives per WCAG 2.1 AA. |
️ Technical Implementation
1. Component Architecture (ConfidenceDashboard.tsx)
The dashboard is a state-aware React component that conditionally renders metrics based on auction state and user role.
// packages/frontend/src/app/auction/[id]/components/ConfidenceDashboard.tsx
'use client';
import { useAuctionState } from '@lib/hooks/useAuctionState';
import { AuctionState } from '@lib/contracts/types';
import { MetricCard } from '@components/ui/MetricCard';
export function ConfidenceDashboard({ auctionId }: { auctionId: string }) {
const { state, activeBids, timeRemaining, encryptionStatus, isSeller, isWinner } = useAuctionState(auctionId);
return (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4" role="region" aria-label="Auction Confidence Metrics">
{/* Always Visible: Process Transparency */}
<MetricCard
label="Sealed Bids"
value={activeBids}
icon=""
tooltip="Encrypted bids submitted. Values remain hidden until settlement."
/>
<MetricCard
label="Time Until Reveal"
value={formatTime(timeRemaining)}
icon="️"
tooltip="Official decryption window opens at this time."
/>
{/* State-Gated: Encryption Status */}
{state === AuctionState.ACTIVE && (
<MetricCard
label="Your Bid Status"
value={encryptionStatus}
icon="️"
variant={encryptionStatus === 'Confirmed' ? 'success' : 'pending'}
/>
)}
{/* State-Gated: Processing Status */}
{state === AuctionState.RESOLVING && (
<MetricCard
label="Settlement Progress"
value=" CoFHE Processing"
icon="️"
tooltip="Encrypted comparison in progress. Zero plaintext exposure."
/>
)}
{/* State-Gated: Final Outcome (Winner/Seller Only) */}
{state === AuctionState.FINALIZED && (isSeller || isWinner) && (
<MetricCard
label="Result"
value={isWinner ? ' You Won' : ' Winner Declared'}
icon={isWinner ? '' : ''}
variant="success"
/>
)}
{/* Emergency State: Auto-Recovery */}
{state === AuctionState.VOIDED && (
<MetricCard
label="Status"
value="️ Auto-Refund Triggered"
icon=""
variant="warning"
tooltip="Network delay detected. Full liquidity recovery available via claimRefund()."
/>
)}
</div>
);
}2. Terminology Mapping & Progressive Disclosure
Technical complexity is translated into trust-building, non-leaking user language.
| Technical Term | User-Facing Language | Visibility Rule |
|---|---|---|
FHE Encryption | Bid Protected | Always shown during ACTIVE |
CoFHE Processing | ️ Sealed Vault Active | Shown during RESOLVING |
AVS Verification | Verified by Network | Shown during FINALIZED |
Dynamic Timeout | ️ Auto-refund triggered | Shown only during VOIDED |
Ciphertext Hash | Encrypted & Confirmed | Never shows raw hex |
FHE.lte Solvency | Funds Verified | Abstracted; never shows balance |
SlashedPot Distribution | Compensation Pool | Shown only during CANCELLED |
3. Real-Time State Subscription (useAuctionState.ts)
The hook manages WebSocket event subscriptions and local state caching for sub-2-second dashboard updates.
// packages/frontend/src/lib/hooks/useAuctionState.ts
import { useEffect, useState } from 'react';
import { usePublicClient, useWatchContractEvent } from 'wagmi';
import { fairMarketAbi } from '@lib/contracts/abi';
export function useAuctionState(auctionId: string) {
const [state, setState] = useState<AuctionState>(AuctionState.CREATED);
const [activeBids, setActiveBids] = useState(0);
const [timeRemaining, setTimeRemaining] = useState<number>();
const client = usePublicClient();
// Initial fetch
useEffect(() => {
const fetchState = async () => {
const [auctionState, bidCount, endTime] = await client.multicall({
contracts: [
{ address: CONTRACT_ADDRESS, abi: fairMarketAbi, functionName: 'getAuctionState', args: [auctionId] },
{ address: CONTRACT_ADDRESS, abi: fairMarketAbi, functionName: 'getBidCount', args: [auctionId] },
{ address: CONTRACT_ADDRESS, abi: fairMarketAbi, functionName: 'getEndTime', args: [auctionId] },
]
});
setState(auctionState.result as AuctionState);
setActiveBids(Number(bidCount.result));
setTimeRemaining(Number(endTime.result) - Date.now()/1000);
};
fetchState();
}, [auctionId, client]);
// Real-time event subscriptions
useWatchContractEvent({
address: CONTRACT_ADDRESS,
abi: fairMarketAbi,
eventName: 'BidPlaced',
args: { auctionId: BigInt(auctionId) },
onLogs: (logs) => setActiveBids(prev => prev + logs.length)
});
useWatchContractEvent({
address: CONTRACT_ADDRESS,
abi: fairMarketAbi,
eventName: 'AuctionFinalized',
args: { auctionId: BigInt(auctionId) },
onLogs: () => setState(AuctionState.FINALIZED)
});
return { state, activeBids, timeRemaining, encryptionStatus: deriveEncryptionStatus(state) };
}Dashboard Data Flow
️ Security & Privacy Guarantees
- Zero Information Leakage: The dashboard never renders plaintext bid values, winner addresses, or decrypted settlement data before cryptographic finality. Component-level guards and CI lint rules enforce this at build time.
- State-Gated Access Control: Sensitive metrics (e.g., “You Won”) are only visible to authenticated users with verified roles (
isSeller,isWinner), preventing unauthorized information disclosure. - Timing Attack Resistance: By displaying aggregated counts (
activeBids) rather than individual bid timestamps or values, the dashboard neutralizes inference attacks based on UI update patterns. - Accessibility by Design: All metrics include ARIA labels, keyboard navigation support, and color-blind safe palettes, ensuring trust transparency is inclusive and compliant with WCAG 2.1 AA.
- Offline Resilience: Cached state persists via
Zustandstore during network drops, preventing UI flicker or data loss while maintaining privacy boundaries.
Audit Gate Compliance (P0)
Progression through Phase 5 is strictly blocked until all P0 items pass:
- [] Zero Plaintext Rendering: CI lint rules fail the build if any component attempts to display numeric bid values, winner addresses, or decrypted data before
FINALIZED/VOIDEDstate. - [] State-Gated Visibility: Unit tests confirm that
ConfidenceDashboardconditionally renders metrics exactly perAuctionStatetransition rules. - [] Terminology Abstraction: All technical terms (
FHE,CoFHE,AVS) are mapped to user-facing language via a centralizedterminology.tsdictionary; no raw terms appear in UI strings. - [] WebSocket Latency: Dashboard updates reflect on-chain events within
<2sunder normal network conditions; fallback polling activates on WebSocket failure. - [] Accessibility Compliance: Automated axe-core scans confirm WCAG 2.1 AA compliance for all dashboard variants (light/dark mode, mobile/desktop).
- [] Role-Based Access:
isSeller/isWinnerchecks are enforced server-side via signature verification; client-side guards are defense-in-depth only.
Unresolved Points & Explicit Gaps
| Gap / Unresolved Point | Impact | Current Status | Recommended Action |
|---|---|---|---|
| Multi-Language Support | Dashboard strings currently assume English/Arabic. Global accessibility requires i18n. | i18n framework not initialized in Phase 5 specs. | Integrate next-i18next and translation files for top 5 languages before Mainnet. |
| Custom Metric Configuration | Can DAO governance adjust which metrics are displayed or their visibility rules? | Not supported; metrics are hardcoded in component. | Design a governance-controlled DashboardConfig.sol for dynamic metric rules in v2.1. |
| Mobile-Specific Layout | Desktop-centric grid may not optimize for small-screen auction participation. | Tailwind responsive classes exist but no mobile-specific UX testing. | Add mobile E2E tests and viewport-specific component variants in Phase 5 refinement. |
| Historical Data Access | Can users view past auction metrics (e.g., “How many bids did I place last month?”)? | No historical dashboard or analytics layer implemented. | Build a privacy-preserving analytics module using zero-knowledge proofs for aggregate history in v2.2. |
Fact vs. Analysis Distinction: Zero-plaintext rendering, state-gated visibility, terminology abstraction, WebSocket latency targets, and accessibility compliance are verified facts from
Phase 5 Sub-Tasks Matrix(Task 5.4) and theREADME.mdUX 2.0 principles. i18n, configurable metrics, mobile optimization, and historical analytics are unresolved UX enhancements requiring explicit definition before Mainnet launch.
Next Steps
- Proceed to 7.5. Progressive Disclosure to understand the broader design philosophy and terminology abstraction patterns.
- Review Security Model → Data Isolation for detailed component-level guards and CI enforcement strategies.
- Explore Developer Quickstart → Frontend Setup for local environment configuration and dashboard testing instructions.