import { ThreatClient, TradingClient, Chain } from '@webacy-xyz/sdk';
const threatClient = new ThreatClient({
apiKey: process.env.WEBACY_API_KEY,
});
const tradingClient = new TradingClient({
apiKey: process.env.WEBACY_API_KEY,
});
interface TokenScreeningResult {
allowed: boolean;
riskScore: number;
warnings: Warning[];
taxes: { buy: number; sell: number };
liquidity: number;
holderConcentration: number;
recommendation: 'safe' | 'caution' | 'avoid' | 'block';
}
interface Warning {
severity: 'info' | 'warning' | 'critical';
message: string;
category: string;
}
// Main screening function
async function screenToken(
tokenAddress: string,
chain: Chain
): Promise<TokenScreeningResult> {
const warnings: Warning[] = [];
// Parallel fetch all data
const [contract, tax, pools, holders] = await Promise.all([
threatClient.contracts.analyze(tokenAddress, { chain }),
threatClient.contracts.getTax(tokenAddress, { chain }),
tradingClient.tokens.getPools(tokenAddress, { chain }),
tradingClient.holderAnalysis.get(tokenAddress, { chain }).catch(() => null),
]);
// Critical: Honeypot check
if (contract.isHoneypot) {
warnings.push({
severity: 'critical',
message: 'This token appears to be a honeypot. You may not be able to sell.',
category: 'honeypot',
});
return {
allowed: false,
riskScore: 100,
warnings,
taxes: { buy: tax.buyTax, sell: tax.sellTax },
liquidity: 0,
holderConcentration: 0,
recommendation: 'block',
};
}
// Tax warnings
if (tax.sellTax > 25) {
warnings.push({
severity: 'critical',
message: `Extreme sell tax: ${tax.sellTax}%. You will lose most of your value when selling.`,
category: 'tax',
});
} else if (tax.sellTax > 10) {
warnings.push({
severity: 'warning',
message: `High sell tax: ${tax.sellTax}%`,
category: 'tax',
});
}
if (tax.buyTax > 10) {
warnings.push({
severity: 'warning',
message: `High buy tax: ${tax.buyTax}%`,
category: 'tax',
});
}
// Liquidity warnings
const totalLiquidity = pools.pools.reduce((sum, p) => sum + p.liquidity, 0);
if (totalLiquidity < 1000) {
warnings.push({
severity: 'critical',
message: `Very low liquidity: $${totalLiquidity.toLocaleString()}. May be unable to sell.`,
category: 'liquidity',
});
} else if (totalLiquidity < 10000) {
warnings.push({
severity: 'warning',
message: `Low liquidity: $${totalLiquidity.toLocaleString()}. Expect high slippage.`,
category: 'liquidity',
});
}
// Holder concentration warnings
const holderConcentration = holders?.top10Percentage ?? 0;
if (holders) {
if (holders.top10Percentage > 90) {
warnings.push({
severity: 'critical',
message: `Extreme concentration: Top 10 wallets hold ${holders.top10Percentage}%`,
category: 'holders',
});
} else if (holders.top10Percentage > 70) {
warnings.push({
severity: 'warning',
message: `High concentration: Top 10 wallets hold ${holders.top10Percentage}%`,
category: 'holders',
});
}
if (holders.sniperCount > 10) {
warnings.push({
severity: 'warning',
message: `${holders.sniperCount} snipers detected at launch`,
category: 'trading',
});
}
if (holders.bundlerCount > 3) {
warnings.push({
severity: 'warning',
message: `${holders.bundlerCount} bundler wallets detected`,
category: 'trading',
});
}
}
// Determine recommendation
const criticalCount = warnings.filter(w => w.severity === 'critical').length;
const warningCount = warnings.filter(w => w.severity === 'warning').length;
let recommendation: 'safe' | 'caution' | 'avoid' | 'block' = 'safe';
if (criticalCount > 0) {
recommendation = tax.sellTax > 50 ? 'block' : 'avoid';
} else if (warningCount >= 2) {
recommendation = 'caution';
}
return {
allowed: recommendation !== 'block',
riskScore: contract.overallRisk,
warnings,
taxes: { buy: tax.buyTax, sell: tax.sellTax },
liquidity: totalLiquidity,
holderConcentration,
recommendation,
};
}
// Quick screen for Solana (using Trading Lite)
async function quickScreenSolana(tokenMint: string): Promise<{
safe: boolean;
riskScore: number;
isHoneypot: boolean;
liquidity: number;
}> {
const result = await tradingClient.tradingLite.analyze(tokenMint);
return {
safe: result.riskScore < 50 && !result.isHoneypot,
riskScore: result.riskScore,
isHoneypot: result.isHoneypot,
liquidity: result.liquidity,
};
}
// Batch screen for token lists
async function screenTokenList(
tokens: Array<{ address: string; chain: Chain }>
): Promise<Map<string, TokenScreeningResult>> {
const results = new Map<string, TokenScreeningResult>();
// Process in batches of 5 to avoid rate limits
for (let i = 0; i < tokens.length; i += 5) {
const batch = tokens.slice(i, i + 5);
const batchResults = await Promise.all(
batch.map(t => screenToken(t.address, t.chain))
);
batch.forEach((token, index) => {
results.set(token.address, batchResults[index]);
});
}
return results;
}