Earnings
Track your PRIV token earnings and claim rewards from data sharing.
Earnings
The PRIV extension tracks your earnings from data sharing and lets you claim PRIV tokens to your connected wallet.
Earnings Overview
Your earnings come from data you share being purchased on the DataXchange marketplace.
flowchart LR
A[Share Data] --> B[Data Aggregated]
B --> C[Listed on Marketplace]
C --> D[Advertiser Purchases]
D --> E[Revenue Distributed]
E --> F[Your Earnings]Earnings Dashboard
The extension displays your earnings in real-time.
Balance Display
| Field | Description |
|---|---|
| Available Balance | PRIV tokens ready to claim |
| Pending Balance | Earnings being processed |
| Lifetime Earnings | Total earned all time |
Header View
The extension header shows:
- Current available balance
- Pending amount (if any)
- Wallet connection status
- Last sync time
[Screenshot: Extension header showing balance of 127.50 PRIV with pending 12.30 PRIV]
Earnings Data Structure
User Earnings
interface UserEarnings {
totalEarned: number; // Lifetime total
pendingEarnings: number; // Processing
lastClaimDate: string | null; // Last claim
weeklyEarnings: number[]; // Last 7 days
monthlyEstimate: number; // Projected
}User Balance
interface UserBalance {
available: number; // Ready to claim
pending: number; // Being processed
lifetime: number; // All-time total
currency: string; // "PRIV"
}Earnings Tracking
Fetching Earnings
The extension fetches earnings from the background service worker:
// Request earnings from background
const response = await chrome.runtime.sendMessage({
type: 'GET_EARNINGS'
});
if (response.success) {
const earnings = response.data as UserEarnings;
console.log('Total earned:', earnings.totalEarned);
console.log('Pending:', earnings.pendingEarnings);
}Using the Earnings Hook
import { useEarnings } from '@/popup/hooks';
function EarningsDisplay() {
const {
earnings,
stats,
isLoading,
error,
refresh,
claimEarnings
} = useEarnings(walletAddress);
if (isLoading) return <Spinner />;
if (error) return <Error message={error} />;
return (
<div>
<p>Total: {earnings.totalEarned} PRIV</p>
<p>Pending: {earnings.pendingEarnings} PRIV</p>
<button onClick={refresh}>Refresh</button>
</div>
);
}Weekly Earnings Chart
The extension displays a 7-day earnings history.
Chart Data
// Weekly earnings array (last 7 days)
const weeklyEarnings = [
2.5, // 7 days ago
3.2, // 6 days ago
1.8, // 5 days ago
4.1, // 4 days ago
2.9, // 3 days ago
3.7, // 2 days ago
2.3 // Yesterday
];Chart Component
function EarningsHistory({ weeklyEarnings }: { weeklyEarnings: number[] }) {
const maxEarning = Math.max(...weeklyEarnings);
const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
return (
<div className="flex items-end gap-1 h-20">
{weeklyEarnings.map((earning, index) => {
const height = maxEarning > 0
? (earning / maxEarning) * 100
: 0;
return (
<div key={index} className="flex flex-col items-center flex-1">
<div
className="w-full bg-primary rounded-t"
style={{ height: `${height}%` }}
/>
<span className="text-xs mt-1">{days[index]}</span>
</div>
);
})}
</div>
);
}[Screenshot: Weekly earnings bar chart showing daily earnings over 7 days]
Claiming Earnings
Claim Requirements
| Requirement | Value |
|---|---|
| Minimum claim | 10 PRIV |
| Network | Base (mainnet) |
| Gas | Paid by user (~$0.01) |
| Processing time | Instant |
Claim Process
- Ensure you have at least 10 PRIV available
- Click Claim in the extension
- Confirm the transaction in your wallet
- Tokens are sent to your connected address
sequenceDiagram
participant User
participant Extension
participant API as PRIV API
participant Contract as Smart Contract
participant Wallet
User->>Extension: Click Claim
Extension->>API: Request Claim
API->>Contract: Execute Transfer
Contract->>Wallet: Send PRIV Tokens
Contract-->>API: TX Hash
API-->>Extension: Success
Extension->>Extension: Refresh Balance
Extension-->>User: Show SuccessClaim Code
async function claimEarnings(): Promise<boolean> {
if (!walletAddress) {
showError('No wallet connected');
return false;
}
const response = await chrome.runtime.sendMessage({
type: 'CLAIM_EARNINGS',
});
if (response.success) {
const { txHash, amount } = response.data;
showSuccess(`Claimed ${amount} PRIV! TX: ${txHash}`);
await refreshEarnings(); // Update display
return true;
}
showError(response.error ?? 'Claim failed');
return false;
}Earnings Calculation
How Earnings Are Calculated
Your share of each data sale is calculated as:
Your Share = (Your Data Points / Total Pool) × (Sale Price - Platform Fee)Factors Affecting Earnings
| Factor | Impact |
|---|---|
| Data Volume | More data = higher share |
| Data Types | High-value types earn more |
| Data Quality | Diverse patterns valued more |
| Market Demand | Varies by category |
| Competition | More users = smaller shares |
Example Calculation
Dataset sold for: 1000 PRIV
Total contributors: 500 users
Your data points: 2000
Total pool points: 100,000
Your share = (2000 / 100,000) × (1000 - 100)
= 0.02 × 900
= 18 PRIVEarnings by Data Type
Different data types have different values based on market demand.
| Data Type | Base Rate | Market Multiplier | Typical Earnings |
|---|---|---|---|
| Shopping Behavior | High | 1.5x | $5-15/month |
| Social Interactions | High | 1.3x | $4-12/month |
| Ad Impressions | Medium | 1.2x | $3-10/month |
| Search Queries | Medium | 1.1x | $3-8/month |
| Browsing History | Low | 1.0x | $2-6/month |
| Content Preferences | Low | 1.0x | $2-5/month |
| Location Data | Low | 0.8x | $1-4/month |
Caching and Performance
Cache Strategy
Earnings are cached to reduce API calls and improve performance:
interface EarningsCache {
earnings: UserEarnings | null;
balance: UserBalance | null;
lastFetched: number | null;
expiresAt: number | null;
}Cache Duration
| Data | Cache Time |
|---|---|
| Earnings | 5 minutes |
| Balance | 5 minutes |
| Stats | 5 minutes |
Cache Refresh
Cache is automatically refreshed:
- Every 5 minutes via alarm
- After claiming earnings
- After data submission
- On manual refresh
// Invalidate cache after claim
dataCache.lastFetched = 0;
// Force refresh
await chrome.runtime.sendMessage({ type: 'FLUSH_QUEUE' });Error Handling
Empty State
When no wallet is connected or no earnings exist:
const EMPTY_EARNINGS: UserEarnings = {
totalEarned: 0,
pendingEarnings: 0,
lastClaimDate: null,
weeklyEarnings: [0, 0, 0, 0, 0, 0, 0],
monthlyEstimate: 0,
};
const EMPTY_BALANCE: UserBalance = {
available: 0,
pending: 0,
lifetime: 0,
currency: 'PRIV',
};API Errors
The extension handles API errors gracefully:
async function fetchEarnings(walletAddress: string | null) {
// No wallet = return empty (not error)
if (!walletAddress) {
return { success: true, data: EMPTY_EARNINGS };
}
try {
const response = await getEarnings(walletAddress);
if (response.success) {
return response;
}
// Return cached data if available
const cached = await getCachedEarnings();
if (cached) {
return { success: true, data: cached };
}
// Return empty as fallback
return { success: false, error: response.error, data: EMPTY_EARNINGS };
} catch (error) {
return { success: false, error: error.message, data: EMPTY_EARNINGS };
}
}User Statistics
Along with earnings, the extension tracks user statistics:
interface UserStats {
privBalance: number; // Current token balance
totalDataPoints: number; // Data submitted
activeDataTypes: number; // Enabled categories
ranking: number; // Contributor ranking
}Stats Display
function StatsCard({ stats }: { stats: UserStats }) {
return (
<div className="grid grid-cols-2 gap-4">
<Stat
label="Data Points"
value={stats.totalDataPoints.toLocaleString()}
/>
<Stat
label="Active Types"
value={`${stats.activeDataTypes}/7`}
/>
<Stat
label="Ranking"
value={`#${stats.ranking.toLocaleString()}`}
/>
<Stat
label="Balance"
value={`${stats.privBalance.toFixed(2)} PRIV`}
/>
</div>
);
}Maximizing Earnings
Tips for Higher Earnings
- Enable All Data Types: More data types = more earnings
- Browse Consistently: Regular activity increases data volume
- Diverse Browsing: Visit different site categories
- Keep Extension Active: Don't disable data sharing
- Update Regularly: Latest version has optimizations
What Decreases Earnings
- Disabling data types
- Using blocklists heavily
- Infrequent browsing
- Clearing extension data
- Network connectivity issues
Next Steps
- Wallet Integration - Connect to claim
- Data Types - Optimize what you share
- Troubleshooting - Fix earnings issues