UNPKG

@pump-fun/shared-contracts

Version:

Shared contracts for Pump.fun microservices.

410 lines (323 loc) 10.6 kB
# @pump-fun/shared-contracts Shared contracts and type definitions for the Pump.fun notification system microservices. ## Installation ```bash bun add @pump-fun/shared-contracts ``` ## NATS Subjects The package exports NATS subject constants organized by service domain: ### Trade Subjects ```typescript import { TRADE_SUBJECTS } from "@pump-fun/shared-contracts/events"; // Subscribe to all unified trades nats.subscribe(TRADE_SUBJECTS.UNIFIED_TRADE); ``` ### Watcher Service Subjects ```typescript import { TRENDING_SUBJECTS, WATCHLIST_SUBJECTS, PORTFOLIO_SUBJECTS, MILESTONE_SUBJECTS } from "@pump-fun/shared-contracts/events"; // Subscribe to trending events nats.subscribe(TRENDING_SUBJECTS.EVENT); // Subscribe to watchlist alerts nats.subscribe(WATCHLIST_SUBJECTS.ALERT); // Subscribe to portfolio alerts nats.subscribe(PORTFOLIO_SUBJECTS.ALERT); // Subscribe to milestone events nats.subscribe(MILESTONE_SUBJECTS.EVENT); ``` ### Notification Processor Subjects ```typescript import { NOTIFICATION_SUBJECTS } from "@pump-fun/shared-contracts/events"; // Subscribe to all notification events nats.subscribe(NOTIFICATION_SUBJECTS.ALL); // Publish processed notification nats.publish(NOTIFICATION_SUBJECTS.PROCESSED, notification); ``` ### Gateway Subjects ```typescript import { EMAIL_GATEWAY_SUBJECTS, FCM_GATEWAY_SUBJECTS } from "@pump-fun/shared-contracts/events"; // Send email command nats.publish(EMAIL_GATEWAY_SUBJECTS.SEND, emailCommand); // Send push notification nats.publish(FCM_GATEWAY_SUBJECTS.SEND, pushCommand); ``` ## Message Types All message types are defined using Zod schemas for runtime validation: ### Trade Messages ```typescript import { UnifiedTradeSchema, type UnifiedTrade } from "@pump-fun/shared-contracts/events"; // Validate incoming trade const trade = UnifiedTradeSchema.parse(message.data); // Type-safe trade object const processTrade = (trade: UnifiedTrade) => { console.log(`${trade.type} trade: ${trade.amountUsd} USD for ${trade.mintAddress}`); }; ``` ### Alert Messages ```typescript import { TrendingEventSchema, WatchlistAlertSchema, PortfolioAlertSchema, MilestoneEventSchema } from "@pump-fun/shared-contracts/events"; // Parse and validate alerts const trendingEvent = TrendingEventSchema.parse(data); const watchlistAlert = WatchlistAlertSchema.parse(data); const portfolioAlert = PortfolioAlertSchema.parse(data); const milestoneEvent = MilestoneEventSchema.parse(data); ``` ### Notification Messages ```typescript import { ProcessedNotificationSchema, NotificationDeliveryStatusSchema } from "@pump-fun/shared-contracts/events"; // Create a processed notification const notification = ProcessedNotificationSchema.parse({ notificationId: "123", userId: "user-456", type: "watchlist_alert", title: "Price Alert", body: "BONK reached your target price", channels: ["push", "email"], priority: "high", data: { tokenMint: "...", price: 0.001 }, createdAt: Date.now() }); ``` ## Utility Functions ### Message Envelopes ```typescript import { createMessageEnvelope } from "@pump-fun/shared-contracts/events"; // Wrap data in a message envelope with headers const envelope = createMessageEnvelope( { type: "buy", amount: 1000 }, { correlationId: "req-123", userId: "user-456", source: "watchlist-watcher" } ); ``` ### Subject Matching ```typescript import { subjectMatches } from "@pump-fun/shared-contracts/events"; // Check if a subject matches a pattern subjectMatches("trade.unified", "trade.*"); // true subjectMatches("notification.push", "notification.>"); // true subjectMatches("trending.event", "watchlist.*"); // false ``` ### Subject Builder ```typescript import { SubjectBuilder } from "@pump-fun/shared-contracts/events"; // Build subjects dynamically const subject = SubjectBuilder .create("notification") .add("user") .add(userId) .add("push") .build(); // "notification.user.123.push" ``` ### Error Handling ```typescript import { NatsError } from "@pump-fun/shared-contracts/events"; try { await nats.publish(subject, data); } catch (error) { throw new NatsError( "PUBLISH_FAILED", `Failed to publish to ${subject}`, error ); } ``` ## Core NATS Usage This system uses core NATS (not JetStream), so subscriptions are simple: ```typescript import { UNIFIED_TRADE_SUBJECTS } from "@pump-fun/shared-contracts/events"; import { connect } from "nats"; // Connect to NATS const nc = await connect({ servers: ["nats://localhost:4222"] }); // Simple subscription const sub = nc.subscribe(UNIFIED_TRADE_SUBJECTS.ALL); // Process messages (async () => { for await (const msg of sub) { try { const tradeData = JSON.parse(msg.data.toString()); // Validate with schema const trade = UnifiedTradeSchema.parse(tradeData); console.log(`Processing ${trade.type} trade:`, { mintAddress: trade.mintAddress, amountUsd: trade.amountUsd, tx: trade.tx }); await processTrade(trade); } catch (error) { console.error("Failed to process trade:", error); } } })(); ``` ## Complete Example ```typescript import { WATCHLIST_SUBJECTS, WatchlistAlertSchema, createMessageEnvelope, NatsError } from "@pump-fun/shared-contracts/events"; import { connect } from "nats"; // Connect to NATS const nc = await connect({ servers: ["nats://localhost:4222"] }); // Subscribe to watchlist alerts const sub = nc.subscribe(WATCHLIST_SUBJECTS.ALERT); (async () => { for await (const msg of sub) { try { // Parse and validate the alert const alert = WatchlistAlertSchema.parse( JSON.parse(msg.data.toString()) ); console.log(`Alert for ${alert.userId}: ${alert.message}`); // Process the message (no ack needed in core NATS) console.log(`Processed alert for ${alert.userId}: ${alert.message}`); } catch (error) { console.error("Failed to process alert:", error); } } })(); // Publish a watchlist alert const alert = { userId: "user-123", tokenMint: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263", symbol: "BONK", alertType: "price_above" as const, threshold: 0.001, currentValue: 0.0012, message: "BONK price exceeded $0.001", timestamp: Date.now() }; try { const envelope = createMessageEnvelope(alert, { source: "watchlist-watcher", userId: alert.userId }); await nc.publish( WATCHLIST_SUBJECTS.ALERT, JSON.stringify(envelope) ); } catch (error) { throw new NatsError( "PUBLISH_FAILED", "Failed to publish watchlist alert", error as Error ); } ``` ## Type Exports The package exports all TypeScript types for the schemas: ```typescript export type UnifiedTrade = z.infer<typeof UnifiedTradeSchema>; export type TrendingEvent = z.infer<typeof TrendingEventSchema>; export type WatchlistAlert = z.infer<typeof WatchlistAlertSchema>; export type PortfolioAlert = z.infer<typeof PortfolioAlertSchema>; export type MilestoneEvent = z.infer<typeof MilestoneEventSchema>; export type ProcessedNotification = z.infer<typeof ProcessedNotificationSchema>; export type NotificationDeliveryStatus = z.infer<typeof NotificationDeliveryStatusSchema>; export type EmailSendCommand = z.infer<typeof EmailSendCommandSchema>; export type PushSendCommand = z.infer<typeof PushSendCommandSchema>; export type HealthCheckRequest = z.infer<typeof HealthCheckRequestSchema>; export type HealthCheckResponse = z.infer<typeof HealthCheckResponseSchema>; export type SystemError = z.infer<typeof SystemErrorSchema>; export type UserPreferences = z.infer<typeof UserPreferencesSchema>; export type DeviceRegistration = z.infer<typeof DeviceRegistrationSchema>; ``` # Shared Contracts This package contains shared API contracts and types for Pump.fun services. ## For Mobile App Developers ### What to Import The notification API exports its router type which you can use to create a fully typed client: ```typescript import type { Router } from "@pump-fun/notification-api"; ``` ### Setting Up the Client You have two options for consuming the notification API: #### Option 1: Using RPC Endpoint (Recommended for oRPC clients) ```typescript import { createORPCClient } from "@orpc/client"; import { RPCLink } from "@orpc/client/fetch"; import type { RouterClient } from "@orpc/server"; import type { Router } from "@pump-fun/notification-api"; function createNotificationApiClient(config: { baseUrl: string; getAuthToken: () => string | null; }): RouterClient<Router> { return createORPCClient( new RPCLink({ url: `${config.baseUrl}/rpc`, headers: () => { const token = config.getAuthToken(); return token ? { Authorization: `Bearer ${token}` } : {}; }, }), ); } ``` #### Option 2: Using OpenAPI/REST Endpoints If you prefer REST semantics or are not using oRPC: ```typescript // Direct REST calls const response = await fetch("https://api.pump.fun/api/mobile/users/me/pnl-tracking", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ deviceInfo: { appVersion: "1.0.0", deviceId: "device-123", platform: "ios", }, }), }); ``` ### Available Endpoints The notification API provides: - **Mobile User P&L Tracking** - `POST /api/mobile/users/me/pnl-tracking` - Setup 7-day P&L tracking - `GET /api/mobile/users/me/pnl-tracking-status` - Check tracking status - **Authentication Demo** (for testing) - `POST /api/auth/generate-token` - Generate test JWT tokens - `GET /api/auth/profile` - Get authenticated user profile - `GET /api/public/info` - Public endpoint (no auth required) - **Health Check** - `GET /api/health` - Check API health status - **Notification Settings** - Various endpoints for managing notification preferences ### Authentication All endpoints except `/api/health` and `/api/public/*` require JWT authentication: ```typescript headers: { Authorization: `Bearer ${yourJwtToken}`, } ``` ### OpenAPI Documentation The full OpenAPI specification is available at: - JSON: `https://api.pump.fun/api/openapi.json` - Interactive UI: `https://api.pump.fun/api/reference` ## Contract-First Development This package also contains contract definitions that can be imported for type safety: ```typescript import { mobileUserContract } from "@pump-fun/shared-contracts/api-contracts"; ``` These contracts define the API specification and can be used to ensure client-server compatibility.