Catalog npm
PACKAGE EXPLORERView in registry

555x402-vap

npm

Verifiable Attention Protocol SDK - Earn rewards for real, verified attention

attentionengagementrewardssolanavapverificationweb3
Versions collected1
Direct dependencies2From release 0.1.0
Last observed20 Oct, 20:25 UTC

README

Current package documentation

@vap/sdk

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

npm version License: 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

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

Quick Start

Vanilla JavaScript

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

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

new VapClient(options: VapOptions)

Options:

interface VapOptions {
  wsUrl: string;  // WebSocket URL with user/channel params
}

Example:

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:

type SignerFunction = (message: Uint8Array) => Promise<Uint8Array>;

Example with Phantom:

client.setSigner(async (msg) => {
  const signature = await window.solana.signMessage(msg, 'utf8');
  return signature.signature;
});

Example with Privy:

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:

type RewardHandler = (amount: number, unit: string) => void;

Example:

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:

client.startVap();
stop(): void

Stops the session and closes WebSocket connection.

Example:

// 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

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

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

// 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):

{
  "nonce": "abc123...",
  "seq": 1,
  "ts": 1699564800000
}

Client → Server (Response):

{
  "userId": "alice",
  "channelId": "stream-789",
  "seq": 1,
  "sig": "base64_signature",
  "pubkey": "base64_public_key",
  "cts": 1699564801000
}

Server → Client (Reward):

{
  "reward": {
    "amount": 0.0001,
    "unit": "SOL"
  }
}

Examples

Next.js App with Privy

'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)

<!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

# 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

License

MIT © 555x402

Support


Earn while you engage 🎯

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 1

Collected releases
VersionPublishedDirect dependenciesOpen version
0.1.0Registry latest11 Nov 20252

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

ACCESSIBLE
render-network-os/555x402-vap-sdk

Shared repository data · GitHub

License detected in repository
Not collected
Last push
11 Nov 2025
Archived
No
Contributor identities
— · partial collection

Public sponsor counts have not been collected. These signals belong to the repository owner.

Downloads & popularity

0count
Gauge · observed snapshots23 Oct 2026
10.50
2026-10-23: 0 · observed 2026-10-23T14:41:15.2113728Z
23 Oct23 Oct

github · 1 observations shown. Missing days are unknown.

View observed values
Date / source periodValueObserved at
2026-10-2302026-10-23T14:41:15.2113728Z

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 20:25 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:f35e7eb7301ee48404cc4597b73987a3c4cb6c593dc544efb35bc097c7be997a