Documentation Index
Fetch the complete documentation index at: https://docs.mobula.io/llms.txt
Use this file to discover all available pages before exploring further.
What You’ll Build
A script that checks whether a token’s liquidity pool is safe by analyzing LP holder distribution — burned, locked in known protocols (Unicrypt, PinkLock, Team Finance, Mudra), held by contracts, or sitting unlocked in regular wallets.
Why It Matters
The #1 rug pull vector is unlocked liquidity. A developer who holds LP tokens can remove all liquidity at any time, crashing the token price to zero. The liquidityAnalysis field gives you a per-pool breakdown of exactly who holds the LP tokens and whether they’re safe.
Quick Start
curl -s "https://api.mobula.io/api/2/token/security?blockchain=ethereum&address=0x6982508145454ce325ddbe47a25d4ec3d2311933" \
-H "Authorization: YOUR_API_KEY" | jq '.data.liquidityAnalysis'
Understanding the Response
The liquidityAnalysis array returns the top 3 pools by 24h volume. Each pool includes a full holder breakdown:
{
"liquidityAnalysis": [
{
"poolAddress": "0xa43fe16908251ee70ef74718545e4fe6c5ccec8f",
"poolType": "uniswap-v2",
"burnedPercentage": 36.4,
"lockedPercentage": 0,
"contractPercentage": 0.8,
"unlockedPercentage": 62.8,
"topHolders": [
{ "address": "0x371d...", "percentage": 31.8, "type": "unlocked", "protocol": null },
{ "address": "0x0000...0000", "percentage": 20.6, "type": "burned", "protocol": null },
{ "address": "0x663a...", "percentage": 16.2, "type": "locked", "protocol": "Unicrypt" }
]
}
]
}
Holder Types
| Type | What It Means | Risk Level |
|---|
burned | LP tokens sent to a dead address. Liquidity is permanently locked. | Safe |
locked | LP tokens held by a locker protocol (Unicrypt, PinkLock, etc.). Locked for a defined period. | Safe (while lock active) |
contract | LP tokens in an unrecognized contract. Could be a custom lock, a multisig, or something else. | Investigate |
unlocked | LP tokens in a regular wallet. Can be removed at any time. | Risk |
Cookbook: Rug Pull Risk Scorer
This TypeScript function takes a token address and returns a risk assessment:
const API_KEY = "YOUR_API_KEY";
interface LiquidityRisk {
riskLevel: "safe" | "moderate" | "high" | "critical";
burnedPct: number;
lockedPct: number;
unlockedPct: number;
topUnlockedHolder: { address: string; percentage: number } | null;
pools: number;
details: string;
}
async function assessLiquidityRisk(
address: string,
blockchain: string
): Promise<LiquidityRisk> {
const res = await fetch(
`https://api.mobula.io/api/2/token/security?address=${address}&blockchain=${blockchain}`,
{ headers: { Authorization: API_KEY } }
);
const { data } = await res.json();
const pools = data.liquidityAnalysis;
if (!pools || pools.length === 0) {
return {
riskLevel: "critical",
burnedPct: 0,
lockedPct: 0,
unlockedPct: 100,
topUnlockedHolder: null,
pools: 0,
details: "No liquidity analysis available",
};
}
// Aggregate across all pools (weighted by position in array = volume rank)
let totalBurned = 0, totalLocked = 0, totalUnlocked = 0;
let topUnlocked: { address: string; percentage: number } | null = null;
for (const pool of pools) {
totalBurned += pool.burnedPercentage;
totalLocked += pool.lockedPercentage;
totalUnlocked += pool.unlockedPercentage;
for (const holder of pool.topHolders) {
if (holder.type === "unlocked") {
if (!topUnlocked || holder.percentage > topUnlocked.percentage) {
topUnlocked = { address: holder.address, percentage: holder.percentage };
}
}
}
}
// Average across pools
const n = pools.length;
const burned = totalBurned / n;
const locked = totalLocked / n;
const unlocked = totalUnlocked / n;
const safePct = burned + locked;
let riskLevel: LiquidityRisk["riskLevel"];
let details: string;
if (safePct >= 90) {
riskLevel = "safe";
details = `${burned.toFixed(1)}% burned + ${locked.toFixed(1)}% locked across ${n} pool(s)`;
} else if (safePct >= 60) {
riskLevel = "moderate";
details = `${unlocked.toFixed(1)}% unlocked — some rug pull risk remains`;
} else if (safePct >= 30) {
riskLevel = "high";
details = `Only ${safePct.toFixed(1)}% of LP is burned/locked — significant rug risk`;
} else {
riskLevel = "critical";
details = `${unlocked.toFixed(1)}% of LP is unlocked — high probability of rug pull`;
}
return { riskLevel, burnedPct: burned, lockedPct: locked, unlockedPct: unlocked, topUnlockedHolder: topUnlocked, pools: n, details };
}
// Usage
const risk = await assessLiquidityRisk("0x6982508145454ce325ddbe47a25d4ec3d2311933", "ethereum");
console.log(`Risk: ${risk.riskLevel} — ${risk.details}`);
Supported Protocols
LP Locker Detection
When LP tokens are held by a recognized locker contract, the protocol field identifies it:
| Protocol | Chains | Description |
|---|
| Unicrypt | Ethereum, BSC, Arbitrum, Base | Largest LP locker, time-locked vaults |
| Team Finance | Ethereum, BSC | Team token and LP locking |
| PinkLock | Ethereum, BSC, Arbitrum, Base | PinkSale ecosystem locker |
| Mudra Locker | BSC | BSC-focused LP locker |
Supported DEX Pools
Liquidity analysis works across all major DEX types:
EVM:
- Uniswap V2/V3/V4, PancakeSwap, Camelot, Balancer, Curve, Solidly/Aerodrome, Fluid
Solana:
- PumpSwap, Raydium (AMM V4, CPMM), Meteora (Dynamic AMM, DBC, DLMM), Orca Whirlpool
Combining with Other Security Signals
For a complete security assessment, combine liquidityAnalysis with other fields from the same endpoint:
const { data } = await res.json();
const redFlags = [];
// Liquidity risk
if (!data.liquidityAnalysis || data.liquidityAnalysis.length === 0) {
redFlags.push("No LP analysis available");
} else if (data.liquidityAnalysis[0].unlockedPercentage > 80) {
redFlags.push(`${data.liquidityAnalysis[0].unlockedPercentage}% LP unlocked`);
}
// Contract risks
if (data.isHoneypot) redFlags.push("Honeypot detected");
if (data.balanceMutable) redFlags.push("Balance can be manipulated");
if (data.isMintable) redFlags.push("Supply can be minted");
if (!data.renounced) redFlags.push("Ownership not renounced");
if (data.sellFeePercentage > 10) redFlags.push(`High sell fee: ${data.sellFeePercentage}%`);
if (data.transferFeePercentage > 10) redFlags.push(`High transfer tax: ${data.transferFeePercentage}%`);
// Holder concentration
if (data.top10HoldingsPercentage > 50) {
redFlags.push(`Top 10 holders control ${data.top10HoldingsPercentage}%`);
}
console.log(redFlags.length === 0 ? "No red flags" : `Red flags: ${redFlags.join(", ")}`);