| name | pump-analyzer-solana |
| description | Real-time monitoring and analytics platform for Pump.fun tokens on Solana using WebSockets, HTML/CSS/JS |
| triggers | ["set up pump analyzer","track pump.fun tokens","monitor solana token launches","add real-time token alerts","integrate pump.fun websocket","build memecoin dashboard","analyze pump.fun token trends","connect solana wallet to tracker"] |
PumpAnalyzer — Solana Token Monitoring Platform
Skill by ara.so — Daily 2026 Skills collection.
PumpAnalyzer is a static front-end platform (pure HTML/CSS/JS, no build tools) that provides real-time monitoring, analytics, and alerts for tokens launched on Pump.fun on the Solana blockchain. It connects to Pump.fun's WebSocket API for sub-second updates, displays price/volume charts, supports custom alert criteria, and includes non-custodial Solana wallet connection.
Installation
git clone https://github.com/happyboy4ty25/pump-analyzer.git
cd pump-analyzer
open index.html
No npm, no bundler, no dependencies — open index.html directly in a browser or serve with any static file server:
python3 -m http.server 8080
npx serve .
Project Structure
pump-analyzer/
├── index.html # Main landing page & app shell
├── css/
│ └── style.css # All styles, animations, responsive layout
├── js/
│ ├── main.js # App init, UI interactions, animations
│ ├── websocket.js # Pump.fun WebSocket connection & event handling
│ ├── wallet.js # Solana wallet adapter (Phantom, Solflare, etc.)
│ ├── alerts.js # Custom alert criteria logic
│ └── charts.js # Price/volume chart rendering
└── assets/
└── ... # Icons, images
Key Concepts & Architecture
1. Pump.fun WebSocket Connection
PumpAnalyzer subscribes to Pump.fun's real-time data stream. The core pattern:
const PUMP_FUN_WS_URL = 'wss://pumpportal.fun/api/data';
class PumpWebSocket {
constructor(onToken, onTrade) {
this.onToken = onToken;
this.onTrade = onTrade;
this.ws = null;
this.reconnectDelay = 1000;
}
connect() {
this.ws = new WebSocket(PUMP_FUN_WS_URL);
this.ws.addEventListener('open', () => {
console.log('[PumpWS] Connected');
this.reconnectDelay = 1000;
this.ws.send(JSON.stringify({
method: 'subscribeNewToken'
}));
this..(.({
: ,
: []
}));
});
..(, {
data = .(event.);
(data. === ) {
.(data);
} (data. === || data. === ) {
.(data);
}
});
..(, {
.(, ., );
( .(), .);
. = .(. * , );
});
..(, {
.(, err);
..();
});
}
() {
(.?. === .) {
..(.({
: ,
: [mintAddress]
}));
}
}
() {
.?.();
}
}
;
2. Handling New Token Events
import PumpWebSocket from './websocket.js';
const tokenList = [];
function onNewToken(tokenData) {
tokenList.unshift(tokenData);
renderTokenCard(tokenData);
checkAlerts(tokenData);
}
function onTrade(tradeData) {
(tradeData., tradeData);
}
pumpWS = (onNewToken, onTrade);
pumpWS.();
3. Rendering Token Cards
function renderTokenCard(token) {
const container = document.getElementById('token-feed');
const card = document.createElement('div');
card.className = 'token-card';
card.dataset.mint = token.mint;
card.innerHTML = `
<div class="token-header">
<img src="${token.imageUri || 'assets/placeholder.png'}"
alt="${token.symbol}"
class="token-image"
onerror="this.src='assets/placeholder.png'">
<div class="token-info">
<span class="token-name">${escapeHtml(token.name)}</span>
<span class="token-symbol">$${escapeHtml(token.symbol)}</span>
</div>
<span class="token-time">${formatTimestamp(token.timestamp)}</span>
</div>
<div class="token-metrics">
<div class="metric">
<label>Market Cap</label>
<span class="market-cap">${formatSol(token.marketCapSol)} SOL</span>
</div>
<div class="metric">
<label>Initial Buy</label>
<span>${formatSol(token.initialBuy)} SOL</span>
</div>
</div>
<div class="token-actions">
<a href="https://pump.fun/${token.mint}" target="_blank" rel="noopener"
class="btn btn-small">View on Pump.fun</a>
<button class="btn btn-small btn-outline"
onclick="setAlert('${token.mint}')">Set Alert</button>
</div>
`;
card.style.opacity = '0';
card.style.transform = 'translateY(-10px)';
container.(card);
( {
card.. = ;
card.. = ;
card.. = ;
});
(container.. > ) {
container.(container.);
}
}
() {
div = .();
div. = str;
div.;
}
() {
amount ? (amount).() : ;
}
() {
(ts * ).();
}
4. Custom Alerts System
const MAX_FREE_ALERTS = 5;
class AlertManager {
constructor() {
this.alerts = JSON.parse(localStorage.getItem('pump_alerts') || '[]');
this.dailyCount = parseInt(localStorage.getItem('pump_alert_count') || '0');
this.plan = localStorage.getItem('pump_plan') || 'free';
}
canAddAlert() {
if (this.plan !== 'free') return true;
return this.dailyCount < MAX_FREE_ALERTS;
}
addAlert({ mint, criteria }) {
if (!this.canAddAlert()) {
showUpgradeModal('You've reached the free plan limit of alerts/day.grantedassets/icon.pump_alert_countpump_alerts
5. Solana Wallet Connection (Non-Custodial)
class SolanaWalletConnect {
constructor() {
this.publicKey = null;
this.provider = null;
}
getProvider() {
if ('phantom' in window && window.phantom?.solana?.isPhantom) {
return window.phantom.solana;
}
if ('solflare' in window && window.solflare?.isSolflare) {
return window.solflare;
}
return null;
}
async connect() {
this.provider = this.getProvider();
if (!this.provider) {
window.open('https://phantom.app/', );
();
}
{
resp = ..();
. = resp..();
.();
.;
} (err) {
(err. === ) {
();
}
err;
}
}
() {
.?.();
. = ;
.();
}
() {
btn = .();
(btn) {
btn. = ;
btn..();
}
.();
}
() {
btn = .();
(btn) {
btn. = ;
btn..();
}
}
() {
= ;
}
}
wallet = ();
.()?.(, () => {
{
(wallet.) {
wallet.();
} {
wallet.();
}
} (err) {
.(, err.);
(err., );
}
});
6. Simple Price Chart (Canvas API)
class PriceChart {
constructor(canvasId) {
this.canvas = document.getElementById(canvasId);
this.ctx = this.canvas.getContext('2d');
this.dataPoints = [];
this.maxPoints = 60;
}
addPoint(marketCapSol, timestamp) {
this.dataPoints.push({ value: marketCapSol, time: timestamp });
if (this.dataPoints.length > this.maxPoints) {
this.dataPoints.shift();
}
this.render();
}
render() {
const { ctx, canvas, dataPoints } = this;
const { width, height } = canvas;
ctx.clearRect(0, 0, width, height);
if (dataPoints.length < 2) ;
values = dataPoints.( p.);
min = .(...values);
max = .(...values);
range = max - min || ;
xStep = width / (dataPoints. - );
gradient = ctx.(, , , height);
gradient.(, );
gradient.(, );
ctx.();
ctx.(, height - ((dataPoints[]. - min) / range) * height);
dataPoints.( {
x = i * xStep;
y = height - ((point. - min) / range) * height;
ctx.(x, y);
});
ctx.(width, height);
ctx.(, height);
ctx.();
ctx. = gradient;
ctx.();
ctx.();
ctx. = ;
ctx. = ;
dataPoints.( {
x = i * xStep;
y = height - ((point. - min) / range) * height;
i === ? ctx.(x, y) : ctx.(x, y);
});
ctx.();
}
}
;
Configuration
All configuration is done via constants at the top of each JS file. No .env file needed for the front-end — but if you add a backend:
const CONFIG = {
WS_URL: 'wss://pumpportal.fun/api/data',
RPC_URL: process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com',
API_BASE: process.env.API_BASE_URL || 'https://pump-analyzer.com/api',
PLANS: {
free: { alertsPerDay: 5, price: 0 },
pro: { alertsPerDay: Infinity, price: 29, sol: 0.5 },
elite: { alertsPerDay: Infinity, price: 99, sol: 1.5 }
}
};
Common Patterns
Filter tokens by keyword on arrival
function onNewToken(token) {
const keyword = document.getElementById('filter-input').value.toLowerCase();
if (keyword && !`${token.name} ${token.symbol}`.toLowerCase().includes(keyword)) return;
renderTokenCard(token);
}
Debounce rapid trade updates
const updateQueue = new Map();
function onTrade(trade) {
clearTimeout(updateQueue.get(trade.mint));
updateQueue.set(trade.mint, setTimeout(() => {
updateTokenMetrics(trade.mint, trade);
updateQueue.delete(trade.mint);
}, 200));
}
Show upgrade modal for free plan limits
function showUpgradeModal(reason) {
document.getElementById('upgrade-reason').textContent = reason;
document.getElementById('upgrade-modal').classList.add('visible');
}
Troubleshooting
| Issue | Cause | Fix |
|---|
| WebSocket won't connect | Browser blocks WSS or wrong URL | Check wss://pumpportal.fun/api/data is reachable; use DevTools Network tab |
| No tokens appearing | Subscription message not sent on open | Ensure subscribeNewToken is sent inside ws.addEventListener('open', ...) |
| Wallet button does nothing | Wallet extension not installed | Detect window.phantom before calling .connect() |
| Notifications not firing | Permission not granted | Call Notification.requestPermission() after a user gesture |
| Cards not updating market cap | mint mismatch between token and trade events | Normalize mint addresses to strings before comparison |
| Page flickers on new token | DOM prepend causes reflow | Use requestAnimationFrame + CSS transitions for card entry |
Pricing / Plan Gating Pattern
function requirePlan(minimumPlan, action) {
const planRank = { free: 0, pro: 1, elite: 2 };
const userPlan = localStorage.getItem('pump_plan') || 'free';
if (planRank[userPlan] >= planRank[minimumPlan]) {
action();
} else {
showUpgradeModal(`This feature requires the ${minimumPlan} plan.`);
}
}
requirePlan('pro', () => enableUnlimitedAlerts());
requirePlan('elite', () => enableAIInsights());
Resources