@pump-fun/shared-contracts
Version:
Shared contracts for Pump.fun microservices.
410 lines (323 loc) • 10.6 kB
Markdown
# @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.