7.7 KiB
7.7 KiB
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
// 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
// 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 completesisFullyVested: Current vesting statusremainingBlocks: 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
// 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
// Check before allowing sensitive actions
async function canPerformAction(vitaID: uint64, action: string): Promise<boolean> {
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
// 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
const config = await collocatio.getConfig();
// config.commitRevealDelay = 10 (blocks)
// config.commitRevealWindow = 1000 (blocks)
// config.wealthConcentration = 500 (basis points = 5%)
Vita Vesting
// 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 |