npm package: 555x402-vap
Description: Verifiable Attention Protocol SDK - Earn rewards for real, verified attention
Author: Render Network OS
Latest version: 0.1.0

# @vap/sdk

> Verifiable Attention Protocol SDK - Earn rewards for real, verified attention on any platform

[![npm version](https://img.shields.io/npm/v/@vap/sdk.svg)](https://www.npmjs.com/package/@vap/sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)

## Overview

VAP (Verifiable Attention Protocol) enables platforms to reward users for genuine attention through cryptographic proof-of-attention. Users earn micro-rewards by proving they're actively engaged through WebSocket heartbeat challenges.

**How it works:**
1. User connects to VAP session via WebSocket
2. Server sends cryptographic challenges every 5 seconds
3. User's wallet signs challenges proving presence
4. Server validates signatures and accumulates attention credits
5. Rewards paid out instantly or accumulated for batch settlement

## Features

- ✅ **Cryptographic Proof**: Ed25519 signature-based attention verification
- ✅ **Real-Time Rewards**: Instant micro-payments for verified attention
- ✅ **State Channels**: Off-chain accumulation with on-chain settlement
- ✅ **Wallet Agnostic**: Works with any Solana wallet or embedded wallet
- ✅ **Framework Agnostic**: Vanilla JS, React, Vue, Svelte
- ✅ **Auto-Reconnect**: Handles disconnections gracefully
- ✅ **TypeScript**: Full type safety

## Installation

```bash
npm install @vap/sdk
# or
pnpm add @vap/sdk
# or  
yarn add @vap/sdk
```

## Quick Start

### Vanilla JavaScript

```typescript
import { VapClient } from '@vap/sdk';

const client = new VapClient({
  wsUrl: 'wss://vap.rendernet.work/session?uid=user123&cid=channel456'
});

// Set up wallet signer
client.setSigner(async (message) => {
  // Sign with your wallet
  const signature = await wallet.signMessage(message);
  return signature;
});

// Handle rewards
client.onReward((amount, unit) => {
  console.log(`Earned ${amount} ${unit}!`);
  // Update UI, show notification, etc.
});

// Start session
client.startVap();

// Stop when user leaves
client.stop();
```

### React Integration

```tsx
import { VapClient } from '@vap/sdk';
import { useWallet } from '@solana/wallet-adapter-react';
import { useEffect, useState } from 'react';

export function useVapSession(userId: string, channelId: string) {
  const { signMessage, publicKey } = useWallet();
  const [totalRewards, setTotalRewards] = useState(0);
  const [client, setClient] = useState<VapClient | null>(null);
  
  useEffect(() => {
    if (!publicKey || !signMessage) return;
    
    const vapClient = new VapClient({
      wsUrl: `wss://vap.rendernet.work/session?uid=${userId}&cid=${channelId}`
    });
    
    // Configure signer
    vapClient.setSigner(async (msg) => {
      const sig = await signMessage(msg);
      return sig;
    });
    
    // Handle rewards
    vapClient.onReward((amount, unit) => {
      setTotalRewards(prev => prev + amount);
      console.log(`+${amount} ${unit}`);
    });
    
    // Start session
    vapClient.startVap();
    setClient(vapClient);
    
    // Cleanup on unmount
    return () => vapClient.stop();
  }, [publicKey, signMessage, userId, channelId]);
  
  return { totalRewards, isActive: !!client };
}

// Usage in component:
function VideoPlayer() {
  const { totalRewards } = useVapSession('user-123', 'video-456');
  
  return (
    <div>
      <video src="..." />
      <div className="rewards-badge">
        Earned: ${totalRewards.toFixed(4)} SOL
      </div>
    </div>
  );
}
```

## API Reference

### Class: `VapClient`

#### Constructor

```typescript
new VapClient(options: VapOptions)
```

**Options:**
```typescript
interface VapOptions {
  wsUrl: string;  // WebSocket URL with user/channel params
}
```

**Example:**
```typescript
const client = new VapClient({
  wsUrl: 'wss://vap.rendernet.work/session?uid=alice&cid=stream-789'
});
```

#### Methods

##### `setSigner(fn: SignerFunction): void`

Configures the message signing function.

**Parameters:**
```typescript
type SignerFunction = (message: Uint8Array) => Promise<Uint8Array>;
```

**Example with Phantom:**
```typescript
client.setSigner(async (msg) => {
  const signature = await window.solana.signMessage(msg, 'utf8');
  return signature.signature;
});
```

**Example with Privy:**
```typescript
import { useSolanaWallets } from '@privy-io/react-auth/solana';

const { wallets } = useSolanaWallets();
const wallet = wallets[0];

client.setSigner(async (msg) => {
  const sig = await wallet.signMessage(msg);
  return new Uint8Array(sig);
});
```

##### `onReward(handler: RewardHandler): void`

Sets the reward callback handler.

**Parameters:**
```typescript
type RewardHandler = (amount: number, unit: string) => void;
```

**Example:**
```typescript
client.onReward((amount, unit) => {
  // Update state
  setEarnings(prev => prev + amount);
  
  // Show toast notification
  toast.success(`+${amount} ${unit}`);
  
  // Track analytics
  analytics.track('reward_earned', { amount, unit });
});
```

##### `startVap(): void`

Initiates the VAP session and WebSocket connection.

**Example:**
```typescript
client.startVap();
```

##### `stop(): void`

Stops the session and closes WebSocket connection.

**Example:**
```typescript
// When user navigates away
window.addEventListener('beforeunload', () => {
  client.stop();
});
```

##### `connect(): void`

Manually connect/reconnect to WebSocket (called automatically by `startVap()`).

## Advanced Usage

### Auto-Reconnect with Exponential Backoff

```typescript
import { VapClient } from '@vap/sdk';

class ResilientVapClient extends VapClient {
  private reconnectAttempts = 0;
  private maxReconnectDelay = 30000;
  
  connect() {
    super.connect();
    
    if (this.ws) {
      this.ws.onclose = () => {
        const delay = Math.min(
          1000 * Math.pow(2, this.reconnectAttempts),
          this.maxReconnectDelay
        );
        
        console.log(`Reconnecting in ${delay}ms...`);
        
        setTimeout(() => {
          this.reconnectAttempts++;
          this.connect();
        }, delay);
      };
      
      this.ws.onopen = () => {
        this.reconnectAttempts = 0;
        console.log('VAP connected');
      };
    }
  }
}
```

### Session Analytics

```typescript
class AnalyticsVapClient extends VapClient {
  private sessionStart = Date.now();
  private heartbeatsCount = 0;
  private rewardsCount = 0;
  
  constructor(opts: VapOptions) {
    super(opts);
    
    // Track heartbeats
    this.onMessage((data) => {
      if (data.nonce) {
        this.heartbeatsCount++;
      }
    });
    
    // Track rewards
    this.onReward((amount) => {
      this.rewardsCount++;
    });
  }
  
  getSessionStats() {
    return {
      duration: Date.now() - this.sessionStart,
      heartbeats: this.heartbeatsCount,
      rewards: this.rewardsCount,
      avgRewardInterval: this.heartbeatsCount / (this.rewardsCount || 1)
    };
  }
}
```

### Multiple Concurrent Sessions

```typescript
// User watching multiple streams simultaneously
const sessions = [
  new VapClient({ wsUrl: 'wss://vap.../session?cid=stream1' }),
  new VapClient({ wsUrl: 'wss://vap.../session?cid=stream2' }),
  new VapClient({ wsUrl: 'wss://vap.../session?cid=stream3' })
];

// Configure all with same signer
sessions.forEach(client => {
  client.setSigner(walletSigner);
  client.onReward((amt, unit) => {
    console.log(`Earned ${amt} ${unit} from ${client.opts.wsUrl}`);
  });
  client.startVap();
});

// Stop all on page unload
window.addEventListener('beforeunload', () => {
  sessions.forEach(c => c.stop());
});
```

## WebSocket Protocol

### Connection

```
wss://vap.rendernet.work/session?uid=<userId>&cid=<channelId>
```

**Query Parameters:**
- `uid` - Unique user identifier
- `cid` - Channel/content identifier

### Message Flow

**Server → Client (Challenge):**
```json
{
  "nonce": "abc123...",
  "seq": 1,
  "ts": 1699564800000
}
```

**Client → Server (Response):**
```json
{
  "userId": "alice",
  "channelId": "stream-789",
  "seq": 1,
  "sig": "base64_signature",
  "pubkey": "base64_public_key",
  "cts": 1699564801000
}
```

**Server → Client (Reward):**
```json
{
  "reward": {
    "amount": 0.0001,
    "unit": "SOL"
  }
}
```

## Examples

### Next.js App with Privy

```tsx
'use client';

import { VapClient } from '@vap/sdk';
import { usePrivy, useSolanaWallets } from '@privy-io/react-auth';
import { useEffect, useState } from 'react';

export default function VideoWithRewards() {
  const { authenticated, login } = usePrivy();
  const { wallets } = useSolanaWallets();
  const [earnings, setEarnings] = useState(0);
  
  useEffect(() => {
    if (!authenticated || wallets.length === 0) return;
    
    const wallet = wallets[0];
    const client = new VapClient({
      wsUrl: `wss://vap.rendernet.work/session?uid=${wallet.address}&cid=video-123`
    });
    
    client.setSigner(async (msg) => {
      const sig = await wallet.signMessage(msg);
      return new Uint8Array(sig);
    });
    
    client.onReward((amount) => {
      setEarnings(prev => prev + amount);
    });
    
    client.startVap();
    
    return () => client.stop();
  }, [authenticated, wallets]);
  
  if (!authenticated) {
    return (
      <button onClick={login} className="...">
        Connect to Earn Rewards
      </button>
    );
  }
  
  return (
    <div>
      <video src="content.mp4" controls />
      <div className="earnings">
        You've earned: ${earnings.toFixed(4)}
      </div>
    </div>
  );
}
```

### Vanilla JS (No Framework)

```html
<!DOCTYPE html>
<html>
<head>
  <script type="module">
    import { VapClient } from 'https://esm.sh/@vap/sdk';
    
    const client = new VapClient({
      wsUrl: 'wss://vap.rendernet.work/session?uid=user&cid=page'
    });
    
    // Setup signer (Phantom example)
    client.setSigner(async (msg) => {
      const resp = await window.solana.signMessage(msg, 'utf8');
      return resp.signature;
    });
    
    // Handle rewards
    client.onReward((amount, unit) => {
      document.getElementById('earnings').textContent = 
        `Earned: ${amount} ${unit}`;
    });
    
    // Start on page load
    client.startVap();
    
    // Stop on page unload
    window.addEventListener('beforeunload', () => client.stop());
  </script>
</head>
<body>
  <div id="earnings">Earned: 0 SOL</div>
  <div>Watch content to earn...</div>
</body>
</html>
```

## Configuration

### Environment Variables

```bash
# WebSocket endpoint (optional, defaults to production)
NEXT_PUBLIC_VAP_WS_URL=wss://vap.rendernet.work/session

# User identifier
NEXT_PUBLIC_USER_ID=user-abc-123

# Channel identifier  
NEXT_PUBLIC_CHANNEL_ID=content-xyz-789
```

## Troubleshooting

### WebSocket Connection Fails

**Symptom**: `onclose` fires immediately after `connect()`

**Solutions:**
- Check network connectivity
- Verify WebSocket URL format
- Check CORS headers (should allow your origin)
- Ensure uid and cid params are present

### Signature Verification Fails

**Symptom**: No rewards despite heartbeats

**Solutions:**
- Verify `setSigner` is called before `startVap()`
- Check wallet is connected and can sign messages
- Ensure signature format is raw bytes (Uint8Array)
- Test with `console.log` in signer function

### Rewards Not Triggering

**Symptom**: Connected but `onReward` never fires

**Solutions:**
- Verify server-side VAP session is active
- Check minimum attention threshold (usually 30 seconds)
- Ensure channel has rewards enabled
- Check server logs for signature validation errors

## Security

### Message Signing

- All heartbeat responses must be signed with user's private key
- Server validates Ed25519 signatures on-chain public key
- Replay attacks prevented via sequence numbers and timestamps
- No private keys transmitted over WebSocket

### Privacy

- No PII transmitted (only wallet addresses)
- Session data encrypted in transit (WSS)
- Minimal fingerprinting (only UA for fraud detection)

## Performance

**Typical metrics:**
- WebSocket latency: <100ms
- Heartbeat interval: 5 seconds
- Signature verification: <10ms
- Reward accumulation: off-chain (instant)
- Settlement: batched every 5 minutes or on-demand

## Changelog

### v0.1.0 (2025-11-11)
- Initial release
- WebSocket client with heartbeat challenges
- Reward handler
- TypeScript support

## Contributing

See [CONTRIBUTING.md](./CONTRIBUTING.md)

## License

MIT © 555x402

## Support

- **Docs**: https://docs.rendernet.work/vap
- **Issues**: https://github.com/Render-Network-OS/555x402-vap-sdk/issues
- **Discord**: https://discord.gg/555x402

## Related Packages

- [@555x402/agg](https://www.npmjs.com/package/@555x402/agg) - Payment aggregation
- [@555x402/hyperlink](https://www.npmjs.com/package/@555x402/hyperlink) - Payment links

---

**Earn while you engage** 🎯