npm package: @ponzinomics/sdk
Description: Official SDK for Ponzinomics - Web3 engagement platform
Author: Ponzinomics
Latest version: 1.0.0

# @sypher/sdk

Official TypeScript SDK for the Sypher platform. Manage authentication, projects, quests, memes, and leaderboards for your Web3 community.

[![npm version](https://img.shields.io/npm/v/@sypher/sdk.svg)](https://www.npmjs.com/package/@sypher/sdk)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.6-blue.svg)](https://www.typescriptlang.org/)
[![Tests](https://img.shields.io/badge/tests-51%20passing-brightgreen.svg)](./src/client/__tests__)

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

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

## Quick Start

The SDK supports two authentication modes:

### Developer Mode (Ponzinomics API)

Use API keys for project-scoped operations (memes, quests, points):

```typescript
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):

```typescript
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

```typescript
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

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

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

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

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

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

```typescript
// 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)

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

```typescript
// 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:

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

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

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

```typescript
// 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:

```typescript
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:

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

```typescript
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

1. **Quest Types**

   ```typescript
   // Before
   quest.pointsReward; // ❌
   quest.status = 'INACTIVE'; // ❌

   // After
   quest.points; // ✅
   quest.status = 'PAUSED'; // ✅
   ```

2. **Points Types**

   ```typescript
   // Before
   balance.balance; // ❌
   rank.totalUsers; // ❌

   // After
   balance.points; // ✅
   rank.total; // ✅
   ```

3. **Award/Deduct Points**

   ```typescript
   // 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](./examples/) directory for complete working examples:

- [Basic Usage](./examples/basic.ts)
- [Quest Management](./examples/quests.ts)
- [User Authentication](./examples/auth.ts)
- [Project Setup](./examples/projects.ts)

---

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

```bash
# Install dependencies
pnpm install

# Run tests
pnpm test

# Build
pnpm build

# Lint
pnpm lint
```

---

## Support

- 📧 Email: support@sypher.io
- 💬 Discord: [Join our community](https://discord.gg/sypher)
- 📖 Docs: [docs.sypher.io](https://docs.sypher.io)
- 🐛 Issues: [GitHub Issues](https://github.com/sypher/sdk/issues)

---

## License

MIT © Sypher

---

## Changelog

See [CHANGELOG.md](./CHANGELOG.md) for release history.