Skip to content

Commit db2184b

Browse files
Jpatchingclaude
andcommitted
3 free scans/day, admin bypass, improved caching
- Rate limit: 3 scans/day (was 10/hour), 24hr TTL - Admin wallet gets unlimited scans - Scan cache: 30min TTL (was 5min) - DexScreener cache: 2hr TTL (was 1hr) - Fix safety net adding wallet address as token when deployer route receives a wallet address instead of a token Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f8303f0 commit db2184b

5 files changed

Lines changed: 29 additions & 19 deletions

File tree

backend/src/middleware/auth.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ export function requireAuth(req: Request, res: Response, next: NextFunction): vo
3131
if (!checkRateLimit(wallet)) {
3232
const remaining = getRemainingScans(wallet);
3333
res.status(429).json({
34-
error: 'Rate limit exceeded. Max 10 scans per hour.',
34+
error: 'Daily scan limit reached. 3 free scans per day.',
3535
remaining,
36-
retry_after: '1 hour',
36+
retry_after: '24 hours',
3737
});
3838
return;
3939
}

backend/src/routes/deployer.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { TTLCache } from '../services/cache';
1515
import type { DeployerScan, DeployerToken, FundingInfo, ScanEvidence, ScanConfidence, ScanUsage } from '../types';
1616

1717
const router = Router();
18-
const scanCache = new TTLCache<DeployerScan>(300); // 5 min TTL
18+
const scanCache = new TTLCache<DeployerScan>(1800); // 30 min TTL — saves Helius + DexScreener calls
1919

2020
router.get('/:token_address', async (req: Request, res: Response) => {
2121
const token_address = req.params.token_address as string;
@@ -52,8 +52,10 @@ router.get('/:token_address', async (req: Request, res: Response) => {
5252
// Step 3: Find all tokens this deployer created via Pump.fun
5353
const deployerTokenMints = await findDeployerTokens(deployerWallet);
5454

55-
// Safety net: always include the scanned token if not already found
56-
if (!deployerTokenMints.includes(token_address)) {
55+
// Safety net: include the scanned token if not already found,
56+
// but only if the deployer is DIFFERENT from the token address.
57+
// (If they're the same, the user entered a wallet address, not a token.)
58+
if (deployerWallet !== token_address && !deployerTokenMints.includes(token_address)) {
5759
deployerTokenMints.unshift(token_address);
5860
}
5961

backend/src/routes/wallet.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { TTLCache } from '../services/cache';
1414
import type { DeployerScan, DeployerToken, FundingInfo, ScanEvidence, ScanConfidence, ScanUsage } from '../types';
1515

1616
const router = Router();
17-
const walletCache = new TTLCache<DeployerScan>(300);
17+
const walletCache = new TTLCache<DeployerScan>(1800); // 30 min TTL
1818

1919
router.get('/:wallet_address', async (req: Request, res: Response) => {
2020
const wallet_address = req.params.wallet_address as string;

backend/src/services/auth.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@ import bs58 from 'bs58';
44
import { TTLCache } from './cache';
55

66
const nonceCache = new TTLCache<string>(300); // 5min TTL for nonces
7-
const usageCache = new TTLCache<number>(3600); // 1hr window for rate limiting
7+
const usageCache = new TTLCache<number>(86400); // 24hr window for rate limiting
88

9-
const MAX_SCANS_PER_HOUR = 10;
9+
const MAX_SCANS_PER_DAY = 3;
10+
11+
// Wallets exempt from rate limits (admin/dev wallets)
12+
const UNLIMITED_WALLETS = new Set([
13+
'5rSwWRfqGvnQaiJpW3sb3YKLbxtjVxgc4yrvrHNeNwE2',
14+
]);
1015

1116
function getJwtSecret(): string {
1217
const secret = process.env.JWT_SECRET;
@@ -64,8 +69,14 @@ export function verifyToken(token: string): string | null {
6469

6570
/** Check if a wallet has remaining rate limit */
6671
export function checkRateLimit(wallet: string): boolean {
72+
if (UNLIMITED_WALLETS.has(wallet)) return true;
6773
const usage = usageCache.get(wallet) || 0;
68-
return usage < MAX_SCANS_PER_HOUR;
74+
return usage < MAX_SCANS_PER_DAY;
75+
}
76+
77+
/** Check if a wallet has unlimited access */
78+
export function isUnlimited(wallet: string): boolean {
79+
return UNLIMITED_WALLETS.has(wallet);
6980
}
7081

7182
/** Increment scan usage for a wallet */
@@ -77,7 +88,7 @@ export function incrementUsage(wallet: string): void {
7788
/** Get remaining scans for a wallet */
7889
export function getRemainingScans(wallet: string): number {
7990
const usage = usageCache.get(wallet) || 0;
80-
return Math.max(0, MAX_SCANS_PER_HOUR - usage);
91+
return Math.max(0, MAX_SCANS_PER_DAY - usage);
8192
}
8293

8394
/** Get current usage count for a wallet */
@@ -86,4 +97,4 @@ export function getUsageCount(wallet: string): number {
8697
}
8798

8899
/** The max scans per hour constant */
89-
export const SCANS_LIMIT = MAX_SCANS_PER_HOUR;
100+
export const SCANS_LIMIT = MAX_SCANS_PER_DAY;

backend/src/services/dexscreener.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { TTLCache } from './cache';
44
const DEXSCREENER_API = 'https://api.dexscreener.com/latest/dex';
55
const ALIVE_LIQUIDITY_THRESHOLD = 100; // USD
66

7-
// Cache token status for 1 hour to avoid redundant DexScreener calls
8-
const dexCache = new TTLCache<TokenStatus>(3600);
7+
// Cache token status for 2 hours to reduce DexScreener API calls
8+
const dexCache = new TTLCache<TokenStatus>(7200);
99

1010
interface TokenStatus {
1111
alive: boolean;
@@ -81,7 +81,7 @@ export async function bulkCheckTokens(
8181
try {
8282
const res = await fetch(`${DEXSCREENER_API}/tokens/${joined}`);
8383
if (!res.ok) {
84-
batch.forEach(addr => results.set(addr, { alive: false, liquidity: 0, volume24h: 0, name: '', symbol: '', pairCreatedAt: null }));
84+
// Don't cache HTTP errors — leave tokens out of results for retry
8585
continue;
8686
}
8787

@@ -129,11 +129,8 @@ export async function bulkCheckTokens(
129129
dexCache.set(addr, status);
130130
}
131131
} catch {
132-
batch.forEach(addr => {
133-
const dead = { alive: false, liquidity: 0, volume24h: 0, name: '', symbol: '', pairCreatedAt: null };
134-
results.set(addr, dead);
135-
dexCache.set(addr, dead);
136-
});
132+
// Don't cache error results — leave errored tokens out of results
133+
// so they're retried next request and not falsely marked as dead
137134
}
138135

139136
// Small delay between batches to avoid rate limiting

0 commit comments

Comments
 (0)