@ponzinomics/sdk
v1.0.0
Published
Official SDK for Ponzinomics - Web3 engagement platform
Maintainers
Readme
@sypher/sdk
Official TypeScript SDK for the Sypher platform. Manage authentication, projects, quests, memes, and leaderboards for your Web3 community.
Features
- 🔐 Dual Authentication - JWT (user-scoped) and API Key (project-scoped)
- 🎨 Meme Generation - AI-powered meme creation
- 🎯 Quest System - Track Twitter, Telegram, Wallet, and custom quests
- 🏆 Leaderboards - Points, ranks, and rewards
- 👤 User Management - Profiles and authentication
- 📁 Project Management - Teams and API keys
- 💯 TypeScript First - Full type safety
- ✅ Fully Tested - 51 tests covering all features
Installation
npm install @sypher/sdk
# or
pnpm add @sypher/sdk
# or
yarn add @sypher/sdkQuick Start
The SDK supports two authentication modes:
Developer Mode (Ponzinomics API)
Use API keys for project-scoped operations (memes, quests, points):
import { Sypher } from '@sypher/sdk';
const sypher = new Sypher({
apiKey: 'sypher_live_xxx',
projectId: 'proj_xxx',
});
// Generate a meme
const meme = await sypher.meme.generate({
prompt: 'When the code works on the first try',
});
// Complete a quest
await sypher.quests.complete('quest_123', {
userId: 'user_456',
proof: { twitterId: '123456789' },
});
// Get leaderboard
const leaders = await sypher.points.getLeaderboard({ limit: 10 });User Mode (Core API)
Use JWT tokens for user-scoped operations (auth, profile, projects):
import { Sypher } from '@sypher/sdk';
const sypher = new Sypher({
accessToken: 'jwt_token_xxx',
});
// Login
const { user, accessToken } = await sypher.auth.login({
privyToken: 'privy_xxx',
});
// Get user profile
const profile = await sypher.users.getMe();
// List projects
const projects = await sypher.projects.list();
// Create API key
const { rawKey } = await sypher.apiKeys.create('proj_123', {
name: 'Production Key',
});Configuration
const sypher = new Sypher({
// Ponzinomics API (project-scoped)
apiKey: 'sypher_live_xxx', // Optional
projectId: 'proj_xxx', // Optional
// Core API (user-scoped)
accessToken: 'jwt_token_xxx', // Optional
// Common
baseUrl: 'https://api.sypher.io', // Optional (default shown)
timeout: 30000, // Optional (default: 30s)
});Note: You must provide either (apiKey + projectId) or accessToken.
Core API (User Authentication)
Authentication
// Login with Privy
const { user, accessToken, refreshToken, expiresIn } = await sypher.auth.login({
privyToken: 'privy_xxx',
});
// Refresh token
const { accessToken, refreshToken } = await sypher.auth.refresh({
refreshToken: 'refresh_xxx',
});
// Get current user
const user = await sypher.auth.getMe();User Management
// Get current user with projects
const user = await sypher.users.getMe();
console.log(user.ownedProjects);
console.log(user.projectMembers);
// Get user by ID
const otherUser = await sypher.users.get('user_456');
// Update profile
await sypher.users.updateMe({
displayName: 'John Doe',
twitterHandle: 'johndoe',
});
// Delete account
await sypher.users.deleteMe();Project Management
// List projects
const projects = await sypher.projects.list();
// Create project
const project = await sypher.projects.create({
name: 'My Web3 Project',
description: 'Community engagement platform',
website: 'https://example.com',
twitter: '@myproject',
});
// Get project
const project = await sypher.projects.get('proj_123');
// Update project
await sypher.projects.update('proj_123', {
description: 'Updated description',
});
// Delete project
await sypher.projects.delete('proj_123');Team Management
// Get team members
const members = await sypher.projects.getMembers('proj_123');
// Add member
await sypher.projects.addMember('proj_123', {
userId: 'user_456',
role: 'ADMIN',
});
// Update member role
await sypher.projects.updateMemberRole('proj_123', 'member_789', {
role: 'MEMBER',
});
// Remove member
await sypher.projects.removeMember('proj_123', 'member_789');Roles: OWNER, ADMIN, MEMBER
API Key Management
// List API keys
const keys = await sypher.apiKeys.list('proj_123');
// Create API key
const { apiKey, rawKey } = await sypher.apiKeys.create('proj_123', {
name: 'Production Key',
permissions: ['meme:generate', 'quest:read'],
expiresAt: '2025-12-31T23:59:59Z', // Optional
});
console.log('Save this key:', rawKey); // Only shown once!
// Revoke API key
await sypher.apiKeys.revoke('proj_123', 'key_456');Ponzinomics API (Project Features)
Meme Generation
// Generate a meme
const meme = await sypher.meme.generate({
prompt: 'When you finally fix that bug',
});
console.log(meme.id);
console.log(meme.status); // 'GENERATING' | 'READY' | 'FAILED'
// List memes
const memes = await sypher.meme.list({
limit: 20,
offset: 0,
status: 'READY',
});
// Get meme by ID
const meme = await sypher.meme.get('meme_123');
// Get/update configuration
const config = await sypher.meme.getConfig();
await sypher.meme.updateConfig({
maxGenerationsPerDay: 100,
});
// Track engagement
await sypher.meme.like('meme_123');
await sypher.meme.share('meme_123');
// Delete meme
await sypher.meme.delete('meme_123');
// Get analytics
const analytics = await sypher.meme.getAnalytics();
console.log(`Total memes: ${analytics.totalGenerated}`);Quest System
Managing Quests (Admin)
// Create a quest
const quest = await sypher.quests.create({
title: 'Follow us on Twitter',
description: 'Follow @SypherApp to earn 100 points',
type: 'TWITTER_FOLLOW',
points: 100,
requirements: { twitterHandle: '@SypherApp' },
startsAt: '2024-01-01T00:00:00Z',
endsAt: '2024-12-31T23:59:59Z',
maxCompletions: 1000,
});
// Update quest
await sypher.quests.update('quest_123', {
status: 'ACTIVE',
points: 150,
});
// Delete quest
await sypher.quests.delete('quest_123');Quest Status: DRAFT, ACTIVE, PAUSED, COMPLETED, EXPIRED
Listing Quests
// All quests
const quests = await sypher.quests.list();
// Filter by status
const activeQuests = await sypher.quests.list({
status: 'ACTIVE',
limit: 20,
offset: 0,
});
// Filter by type
const twitterQuests = await sypher.quests.list({
type: 'TWITTER_FOLLOW',
});
// Get active quests with user completion status
const quests = await sypher.quests.getActive('user_123');
quests.forEach((quest) => {
console.log(`${quest.title}: ${quest.userCompletion ? 'Completed ✓' : 'Available'}`);
});Completing Quests
Different quest types require different proof formats:
// Twitter Follow/Like/Retweet/Reply
await sypher.quests.complete('quest_123', {
userId: 'user_456',
proof: {
twitterId: '123456789',
twitterHandle: '@username', // Optional
},
});
// Telegram Join
await sypher.quests.complete('quest_123', {
userId: 'user_456',
proof: {
telegramId: '987654321',
telegramUsername: 'username', // Optional
},
});
// Wallet Connect
await sypher.quests.complete('quest_123', {
userId: 'user_456',
proof: {
address: '0x1234...',
signature: '0xabcd...',
message: 'Sign to verify',
},
});
// Token Hold
await sypher.quests.complete('quest_123', {
userId: 'user_456',
proof: {
address: '0x1234...',
},
});
// Custom Quest
await sypher.quests.complete('quest_123', {
userId: 'user_456',
proof: {
customField: 'custom-value',
},
});Quest Types:
| Type | Required Proof |
| ----------------- | --------------------------------- |
| TWITTER_FOLLOW | twitterId |
| TWITTER_LIKE | twitterId |
| TWITTER_RETWEET | twitterId |
| TWITTER_REPLY | twitterId |
| TELEGRAM_JOIN | telegramId |
| WALLET_CONNECT | address, signature, message |
| TOKEN_HOLD | address |
| CUSTOM | Varies by quest |
Tracking Completions
// Get user's completions
const completions = await sypher.quests.getUserCompletions('user_123');
completions.forEach((completion) => {
console.log(`Quest ${completion.questId} completed at ${completion.completedAt}`);
console.log(`Points awarded: ${completion.pointsAwarded}`);
});Points & Leaderboard
// Get user balance
const balance = await sypher.points.getBalance('user_123');
console.log(`Points: ${balance.points}`);
console.log(`Tier: ${balance.tier}`);
// Get leaderboard
const leaderboard = await sypher.points.getLeaderboard({
limit: 100,
offset: 0,
});
leaderboard.forEach((entry) => {
console.log(`#${entry.rank}: ${entry.userId} - ${entry.points} points`);
});
// Get user rank
const rank = await sypher.points.getUserRank('user_123');
console.log(`Rank: #${rank.rank} out of ${rank.total}`);Admin Operations
// Award points
await sypher.points.award({
userId: 'user_123',
points: 100,
reason: 'Quest completion',
referenceId: 'quest_456',
metadata: { questTitle: 'Follow us on Twitter' },
});
// Deduct points
await sypher.points.deduct({
userId: 'user_123',
points: 50,
reason: 'Reward redemption',
referenceId: 'reward_789',
});
// Rebuild leaderboard cache
await sypher.points.rebuildLeaderboard();Error Handling
The SDK provides typed error classes:
import {
Sypher,
SypherApiError,
SypherNetworkError,
SypherTimeoutError,
SypherValidationError,
} from '@sypher/sdk';
try {
await sypher.quests.complete('quest_123', { userId: 'user_456' });
} catch (error) {
if (error instanceof SypherApiError) {
console.log(`API Error: ${error.message}`);
console.log(`Status: ${error.statusCode}`);
console.log(`Code: ${error.code}`);
// Handle specific errors
switch (error.code) {
case 'ALREADY_COMPLETED':
console.log('Quest already completed');
break;
case 'DAILY_LIMIT_REACHED':
console.log('Daily generation limit reached');
break;
case 'UNAUTHORIZED':
console.log('Invalid API key or token');
break;
}
} else if (error instanceof SypherNetworkError) {
console.log('Network error - please try again');
} else if (error instanceof SypherTimeoutError) {
console.log('Request timed out');
} else if (error instanceof SypherValidationError) {
console.log(`Validation error: ${error.field}`);
}
}TypeScript Support
The SDK is fully typed with TypeScript:
import type {
// Config
SypherConfig,
// Auth types
User,
AuthResponse,
TokensResponse,
// User types
UserWithProjects,
UpdateUserDto,
// Project types
Project,
ProjectMember,
CreateProjectDto,
// API Key types
ApiKey,
CreateApiKeyResponse,
// Meme types
Meme,
MemeConfig,
MemeAnalytics,
GenerateMemeOptions,
// Quest types
Quest,
QuestType,
QuestStatus,
QuestCompletion,
CreateQuestDto,
UpdateQuestDto,
CompleteQuestOptions,
TwitterProof,
TelegramProof,
WalletProof,
// Points types
UserPoints,
LeaderboardEntry,
UserRank,
AwardPointsOptions,
DeductPointsOptions,
} from '@sypher/sdk';Health Check
const health = await sypher.healthCheck();
console.log(`API Status: ${health.status}`);
console.log(`Timestamp: ${health.timestamp}`);Migration Guide (v0.0.1 → v0.1.0)
Breaking Changes
Quest Types
// Before quest.pointsReward; // ❌ quest.status = 'INACTIVE'; // ❌ // After quest.points; // ✅ quest.status = 'PAUSED'; // ✅Points Types
// Before balance.balance; // ❌ rank.totalUsers; // ❌ // After balance.points; // ✅ rank.total; // ✅Award/Deduct Points
// Before await sypher.points.award({ userId, amount: 100, reason }); // After await sypher.points.award({ userId, points: 100, reason });
New Features
- ✅ Quest CRUD operations (
create,update,delete) - ✅ Core API integration (auth, users, projects, API keys)
- ✅ Dual authentication mode
- ✅ Additional fields in points operations (
referenceId,metadata)
Examples
See the examples directory for complete working examples:
API Coverage
| Module | Endpoints | SDK Coverage | | ------------ | --------- | ------------ | | Memes | 9 | ✅ 100% | | Quests | 8 | ✅ 100% | | Points | 6 | ✅ 100% | | Auth | 3 | ✅ 100% | | Users | 5 | ✅ 100% | | Projects | 9 | ✅ 100% | | API Keys | 3 | ✅ 100% | | Total | 43 | ✅ 100% |
Development
# Install dependencies
pnpm install
# Run tests
pnpm test
# Build
pnpm build
# Lint
pnpm lintSupport
- 📧 Email: [email protected]
- 💬 Discord: Join our community
- 📖 Docs: docs.sypher.io
- 🐛 Issues: GitHub Issues
License
MIT © Sypher
Changelog
See CHANGELOG.md for release history.
