
Security News
/Research
Wallet-Draining npm Package Impersonates Nodemailer to Hijack Crypto Transactions
Malicious npm package impersonates Nodemailer and drains wallets by hijacking crypto transactions across multiple blockchains.
@pump-fun/shared-contracts
Advanced tools
Shared contracts and type definitions for the Pump.fun notification system microservices.
bun add @pump-fun/shared-contracts
The package exports NATS subject constants organized by service domain:
import { TRADE_SUBJECTS } from "@pump-fun/shared-contracts/events";
// Subscribe to all unified trades
nats.subscribe(TRADE_SUBJECTS.UNIFIED_TRADE);
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);
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);
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);
All message types are defined using Zod schemas for runtime validation:
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}`);
};
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);
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()
});
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"
}
);
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
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"
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
);
}
This system uses core NATS (not JetStream), so subscriptions are simple:
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);
}
}
})();
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
);
}
The package exports all TypeScript types for the schemas:
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>;
This package contains shared API contracts and types for Pump.fun services.
The notification API exports its router type which you can use to create a fully typed client:
import type { Router } from "@pump-fun/notification-api";
You have two options for consuming the notification API:
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}` } : {};
},
}),
);
}
If you prefer REST semantics or are not using oRPC:
// 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",
},
}),
});
The notification API provides:
Mobile User P&L Tracking
POST /api/mobile/users/me/pnl-tracking
- Setup 7-day P&L trackingGET /api/mobile/users/me/pnl-tracking-status
- Check tracking statusAuthentication Demo (for testing)
POST /api/auth/generate-token
- Generate test JWT tokensGET /api/auth/profile
- Get authenticated user profileGET /api/public/info
- Public endpoint (no auth required)Health Check
GET /api/health
- Check API health statusNotification Settings
All endpoints except /api/health
and /api/public/*
require JWT authentication:
headers: {
Authorization: `Bearer ${yourJwtToken}`,
}
The full OpenAPI specification is available at:
https://api.pump.fun/api/openapi.json
https://api.pump.fun/api/reference
This package also contains contract definitions that can be imported for type safety:
import { mobileUserContract } from "@pump-fun/shared-contracts/api-contracts";
These contracts define the API specification and can be used to ensure client-server compatibility.
FAQs
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
/Research
Malicious npm package impersonates Nodemailer and drains wallets by hijacking crypto transactions across multiple blockchains.
Security News
This episode explores the hard problem of reachability analysis, from static analysis limits to handling dynamic languages and massive dependency trees.
Security News
/Research
Malicious Nx npm versions stole secrets and wallet info using AI CLI tools; Socket’s AI scanner detected the supply chain attack and flagged the malware.