# ADR-008 UI Implementation Guide This document provides guidance for front-end developers implementing UI for the ADR-008 security features. ## 1. Commit-Reveal Investment System ### Overview Investments now use a two-phase commit-reveal pattern to prevent front-running attacks. Users must first commit a hash of their investment, wait for a delay period, then reveal the actual amount. ### Flow ``` 1. User decides to invest amount X in opportunity Y 2. Frontend generates random nonce (32 bytes) 3. Frontend computes commitment = SHA256(amount || nonce || investorAddress) 4. User calls commitInvestment(opportunityID, commitment, vitaID) 5. Wait 10+ blocks (configurable: CommitRevealDelay) 6. User calls revealInvestment(commitmentID, amount, nonce) 7. If valid, investment is executed ``` ### Contract Methods #### `commitInvestment(opportunityID: uint64, commitment: bytes32, vitaID: uint64) -> uint64` - Returns: commitmentID - Event: `CommitmentCreated(commitmentID, opportunityID, vitaID, revealDeadline)` #### `revealInvestment(commitmentID: uint64, amount: uint64, nonce: bytes) -> bool` - Must be called after CommitRevealDelay blocks (default: 10) - Must be called before CommitRevealWindow expires (default: 1000 blocks) - Event: `CommitmentRevealed(commitmentID, amount, investmentID)` #### `cancelCommitment(commitmentID: uint64) -> bool` - Can cancel pending commitments - Event: `CommitmentCanceled(commitmentID)` #### `getCommitment(commitmentID: uint64) -> CommitmentInfo | null` - Returns commitment details including status ### Frontend Implementation Notes ```typescript // Generate commitment function createCommitment(amount: bigint, investorAddress: string): { commitment: string, nonce: string } { const nonce = crypto.randomBytes(32); const preimage = Buffer.concat([ Buffer.alloc(8), // amount as uint64 big-endian nonce, Buffer.from(investorAddress, 'hex') ]); // Write amount as big-endian uint64 preimage.writeBigUInt64BE(amount, 0); const commitment = sha256(preimage); return { commitment: commitment.toString('hex'), nonce: nonce.toString('hex') }; } // IMPORTANT: Store nonce securely until reveal! // If user loses nonce, they cannot reveal and funds are locked until expiry ``` ### UI Considerations - **Nonce Storage**: Store nonce in localStorage/sessionStorage with commitmentID as key - **Progress Indicator**: Show blocks remaining until reveal is allowed - **Deadline Warning**: Alert user if reveal deadline is approaching - **Retry Logic**: If reveal fails, nonce is still valid for retry --- ## 2. Whale Concentration Limits ### Overview No single investor can hold more than 5% (configurable) of any investment opportunity's total pool. ### How It Works - When `invest()` is called, the contract checks: ``` (existingInvestment + newAmount) / (totalPool + newAmount) <= WealthConcentration ``` - Default limit: 500 basis points (5%) - Enforced at investment time, not at commitment time ### UI Considerations - **Pre-check**: Before committing, query user's existing investment in opportunity - **Warning**: Show warning if user is approaching concentration limit - **Error Handling**: Handle "investment would exceed whale concentration limit" error gracefully ### Querying Current Investment ```typescript // Get all investments by an investor const investments = await contract.getInvestmentsByInvestor(vitaID); const investmentInOpp = investments.find(inv => inv.opportunityID === targetOppID); const currentAmount = investmentInOpp?.amount || 0n; // Get opportunity details const opp = await contract.getOpportunity(oppID); const totalPool = opp.totalPool; // Calculate max additional investment const maxConcentration = config.wealthConcentration; // e.g., 500 = 5% const maxAllowed = (totalPool * BigInt(maxConcentration)) / 10000n; const maxAdditional = maxAllowed - currentAmount; ``` --- ## 3. Sybil Resistance Vesting ### Overview New Vita tokens have a 30-day vesting period before gaining full rights. This prevents mass creation of fake identities for manipulation. ### Default Vesting Period - 2,592,000 blocks (~30 days at 1 second per block) - Committee can adjust vesting for individual tokens ### Contract Methods #### `isFullyVested(tokenID: uint64) -> bool` - Returns true if current block >= vestedUntil #### `getVestingInfo(tokenID: uint64) -> [vestedUntil, isFullyVested, remainingBlocks] | null` - `vestedUntil`: Block height when vesting completes - `isFullyVested`: Current vesting status - `remainingBlocks`: Blocks until fully vested (0 if already vested) #### `setVesting(tokenID: uint64, vestedUntil: uint32) -> bool` (Committee only) - Can accelerate vesting for verified identities - Can extend vesting for suspicious accounts ### UI Considerations #### Registration Flow ```typescript // After Vita registration, show vesting status const vestingInfo = await vita.getVestingInfo(tokenID); if (!vestingInfo[1]) { // not fully vested const remainingBlocks = vestingInfo[2]; const estimatedTime = remainingBlocks * 1; // 1 second per block showVestingBanner(`Your identity will be fully verified in ~${formatDuration(estimatedTime)}`); } ``` #### Feature Gating Some features may require fully vested Vita: - **Voting**: May require full vesting to prevent vote manipulation - **Large Investments**: Higher limits may require vesting - **Attestation**: May need vesting to attest others ```typescript // Check before allowing sensitive actions async function canPerformAction(vitaID: uint64, action: string): Promise { const isVested = await vita.isFullyVested(vitaID); switch(action) { case 'vote': case 'large_investment': case 'attest_others': return isVested; default: return true; // Basic actions allowed during vesting } } ``` #### Vesting Progress Display ```typescript // Calculate and display vesting progress const vestingInfo = await vita.getVestingInfo(tokenID); const vestedUntil = vestingInfo[0]; const token = await vita.getTokenByID(tokenID); const createdAt = token.createdAt; const totalVestingBlocks = vestedUntil - createdAt; const elapsedBlocks = currentBlock - createdAt; const progressPercent = Math.min(100, (elapsedBlocks / totalVestingBlocks) * 100); ``` --- ## Configuration Values These can be queried from the contracts: ### Collocatio Config ```typescript const config = await collocatio.getConfig(); // config.commitRevealDelay = 10 (blocks) // config.commitRevealWindow = 1000 (blocks) // config.wealthConcentration = 500 (basis points = 5%) ``` ### Vita Vesting ```typescript // Default vesting period: 2,592,000 blocks (~30 days) // Query individual token vesting via getVestingInfo() ``` --- ## Events to Subscribe ### Collocatio Events - `CommitmentCreated(commitmentID, opportunityID, vitaID, revealDeadline)` - `CommitmentRevealed(commitmentID, amount, investmentID)` - `CommitmentCanceled(commitmentID)` ### Vita Events - `VestingUpdated(tokenID, vestedUntil, updatedBy)` --- ## Error Messages | Error | Meaning | UI Action | |-------|---------|-----------| | `commitment already exists` | Duplicate commitment | Show existing commitment | | `commitment not found` | Invalid commitmentID | Check ID is correct | | `reveal too early` | Before delay period | Show countdown | | `reveal window expired` | Past deadline | Commitment forfeited | | `commitment verification failed` | Wrong amount/nonce | Check stored values | | `investment would exceed whale concentration limit` | Too much in one opp | Show max allowed | | `vita is not fully vested` | Vesting incomplete | Show remaining time |