225 lines
7.7 KiB
Markdown
225 lines
7.7 KiB
Markdown
# 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<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
|
|
```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 |
|