meteora_stale_pool_arb.html
· 24 KiB · HTML
Bruto
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Solana Meteora 过期池套利拆解:299 万美金消失的 150 秒</title>
<style>
:root {
--bg: #0d1117;
--panel: #161b22;
--panel2: #1c2330;
--border: #30363d;
--text: #e6edf3;
--text-dim: #8b949e;
--accent: #f7b955;
--accent2: #58a6ff;
--green: #3fb950;
--red: #f85149;
--purple: #bc8cff;
}
* { box-sizing: border-box; }
html, body {
margin: 0; padding: 0;
background: var(--bg);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
"Hiragino Sans GB", "Microsoft YaHei", sans-serif;
line-height: 1.7;
}
.wrap {
max-width: 920px;
margin: 0 auto;
padding: 48px 28px 96px;
}
header {
border-bottom: 1px solid var(--border);
padding-bottom: 28px;
margin-bottom: 36px;
}
.tag {
display: inline-block;
background: rgba(247, 185, 85, 0.12);
color: var(--accent);
padding: 4px 12px;
border-radius: 999px;
font-size: 12px;
letter-spacing: 0.04em;
text-transform: uppercase;
margin-bottom: 16px;
border: 1px solid rgba(247, 185, 85, 0.3);
}
h1 {
font-size: 34px;
line-height: 1.25;
margin: 0 0 12px;
font-weight: 700;
letter-spacing: -0.01em;
}
.subtitle {
color: var(--text-dim);
font-size: 16px;
margin: 0;
}
h2 {
font-size: 22px;
margin: 56px 0 14px;
padding-left: 12px;
border-left: 4px solid var(--accent);
}
h3 {
font-size: 17px;
margin: 28px 0 10px;
color: var(--accent2);
}
p { margin: 12px 0; }
code {
background: var(--panel2);
border: 1px solid var(--border);
padding: 1px 6px;
border-radius: 4px;
font-size: 13.5px;
color: var(--accent);
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.stats {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
margin: 28px 0;
}
.stat {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px;
padding: 18px 16px;
text-align: center;
}
.stat .num {
font-size: 26px;
font-weight: 700;
color: var(--accent);
margin-bottom: 4px;
font-variant-numeric: tabular-nums;
}
.stat .lbl {
color: var(--text-dim);
font-size: 12.5px;
}
.quote {
background: var(--panel);
border-left: 3px solid var(--accent2);
padding: 16px 20px;
border-radius: 0 8px 8px 0;
color: var(--text);
font-size: 15px;
margin: 18px 0;
}
.quote .src {
display: block;
margin-top: 8px;
color: var(--text-dim);
font-size: 12.5px;
}
.panel {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 12px;
padding: 22px 24px;
margin: 22px 0;
}
figure {
margin: 22px 0;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 12px;
padding: 22px 18px 14px;
}
figcaption {
color: var(--text-dim);
font-size: 13px;
text-align: center;
margin-top: 10px;
}
svg { display: block; max-width: 100%; height: auto; margin: 0 auto; }
table {
width: 100%;
border-collapse: collapse;
margin: 18px 0;
font-size: 14px;
}
th, td {
padding: 10px 12px;
border-bottom: 1px solid var(--border);
text-align: left;
}
th {
background: var(--panel2);
color: var(--text-dim);
font-weight: 600;
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.03em;
}
ol.steps {
counter-reset: s;
list-style: none;
padding: 0;
}
ol.steps > li {
counter-increment: s;
position: relative;
padding: 14px 18px 14px 56px;
margin: 10px 0;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px;
}
ol.steps > li::before {
content: counter(s);
position: absolute;
left: 16px;
top: 14px;
width: 28px;
height: 28px;
border-radius: 50%;
background: var(--accent);
color: #1d1300;
font-weight: 700;
text-align: center;
line-height: 28px;
font-size: 14px;
}
.callout {
background: rgba(88, 166, 255, 0.08);
border: 1px solid rgba(88, 166, 255, 0.3);
border-radius: 10px;
padding: 14px 18px;
margin: 18px 0;
color: var(--text);
font-size: 14.5px;
}
.callout.warn {
background: rgba(248, 81, 73, 0.08);
border-color: rgba(248, 81, 73, 0.3);
}
.callout strong { color: var(--accent2); }
.callout.warn strong { color: var(--red); }
footer {
margin-top: 64px;
padding-top: 24px;
border-top: 1px solid var(--border);
color: var(--text-dim);
font-size: 13px;
}
footer a { color: var(--accent2); text-decoration: none; }
footer a:hover { text-decoration: underline; }
@media (max-width: 640px) {
.wrap { padding: 28px 16px 64px; }
h1 { font-size: 26px; }
.stats { grid-template-columns: repeat(2, 1fr); }
}
</style>
</head>
<body>
<div class="wrap">
<header>
<span class="tag">Solana · MEV · DEX 套利</span>
<h1>299 万美金,150 秒蒸发<br/>—— Meteora 过期池套利完整拆解</h1>
<p class="subtitle">用图解搞懂:DLMM 的 bin 是什么,为什么旧 LP 池会变成"白送代币的提款机",以及套利机器人是怎么把它们一口气吞掉的。</p>
</header>
<section>
<div class="stats">
<div class="stat"><div class="num">$2.99M</div><div class="lbl">被抽走金额</div></div>
<div class="stat"><div class="num">150s</div><div class="lbl">总耗时</div></div>
<div class="stat"><div class="num">6 + 1</div><div class="lbl">机器人 + 鲸鱼</div></div>
<div class="stat"><div class="num">10 个月</div><div class="lbl">池子无人维护</div></div>
</div>
<div class="quote">
"$2,993,502 drained from a single Solana pool in 150 seconds. 1 whale. 6 bots. 9 transactions. 1 misconfigured Meteora pool that sat for 10 months."
<span class="src">—— @anshu_code, X / 2026-05-06</span>
</div>
<p>这条推文讲的,是 Solana 链上一个被忽略了 10 个月的 <strong>Meteora DLMM 流动性池</strong>,在 150 秒内被 6 个套利机器人和一个大户合力"洗"了一遍,搬走 299 万美金。本文用图把它拆开讲清楚。</p>
</section>
<h2>1. 它属于哪一类套利?</h2>
<p>这种玩法的术语叫 <strong>Stale Pool Arbitrage(过期池套利)</strong>。靶子是那些 <em>LP 挂上去就不再维护</em> 的集中流动性池——挂的还是几个月前的"老价格",外部市场早已涨飞,池子却毫无觉察。</p>
<figure>
<svg viewBox="0 0 760 280" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="过期池套利示意">
<defs>
<linearGradient id="bg1" x1="0" x2="0" y1="0" y2="1">
<stop offset="0" stop-color="#1c2330"/>
<stop offset="1" stop-color="#161b22"/>
</linearGradient>
<marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto">
<path d="M0,0 L8,3 L0,6 z" fill="#f7b955"/>
</marker>
</defs>
<rect width="760" height="280" fill="url(#bg1)" rx="10"/>
<!-- Raydium box -->
<rect x="40" y="60" width="240" height="160" fill="#0d1117" stroke="#3fb950" stroke-width="1.5" rx="10"/>
<text x="160" y="90" text-anchor="middle" fill="#3fb950" font-size="15" font-weight="700">Raydium / 真实市场</text>
<text x="160" y="135" text-anchor="middle" fill="#e6edf3" font-size="13">代币 X 已涨到</text>
<text x="160" y="170" text-anchor="middle" fill="#3fb950" font-size="32" font-weight="700">$1.00</text>
<text x="160" y="195" text-anchor="middle" fill="#8b949e" font-size="11">活跃交易 · 价格随市场实时更新</text>
<!-- Meteora box -->
<rect x="480" y="60" width="240" height="160" fill="#0d1117" stroke="#f85149" stroke-width="1.5" rx="10"/>
<text x="600" y="90" text-anchor="middle" fill="#f85149" font-size="15" font-weight="700">Meteora 旧池子</text>
<text x="600" y="135" text-anchor="middle" fill="#e6edf3" font-size="13">10 个月没人交易</text>
<text x="600" y="170" text-anchor="middle" fill="#f85149" font-size="32" font-weight="700">$0.001</text>
<text x="600" y="195" text-anchor="middle" fill="#8b949e" font-size="11">价格停在 LP 创建时的水平</text>
<!-- Bridge / arrow -->
<text x="380" y="130" text-anchor="middle" fill="#f7b955" font-size="13" font-weight="700">价差 1000×</text>
<line x1="290" y1="150" x2="470" y2="150" stroke="#f7b955" stroke-width="2" marker-end="url(#arrow)"/>
<text x="380" y="180" text-anchor="middle" fill="#8b949e" font-size="11">套利机器人在这里下手</text>
</svg>
<figcaption>同一种代币,两个池子,价格差 1000 倍。差距在 LP 不维护时会一直存在。</figcaption>
</figure>
<h2>2. 先理解 DLMM:什么是 bin?</h2>
<p>Meteora DLMM(Dynamic Liquidity Market Maker)跟 Uniswap v3 思路一样:把价格切成一格一格的"档位",每一档叫 <strong>bin</strong>。LP 不是在一条曲线上放钱,而是把代币塞进具体某几个 bin 里。</p>
<figure>
<svg viewBox="0 0 760 320" xmlns="http://www.w3.org/2000/svg">
<rect width="760" height="320" fill="url(#bg1)" rx="10"/>
<!-- Active bin marker -->
<line x1="380" y1="40" x2="380" y2="280" stroke="#f7b955" stroke-width="1" stroke-dasharray="4 4"/>
<text x="380" y="32" text-anchor="middle" fill="#f7b955" font-size="13" font-weight="700">↓ Active Bin(当前价)</text>
<!-- Bins -->
<g font-family="ui-monospace, monospace" font-size="11">
<!-- Y bins (left, USDC) -->
<g>
<rect x="60" y="180" width="48" height="100" fill="#3fb950" opacity="0.85" rx="3"/>
<rect x="115" y="160" width="48" height="120" fill="#3fb950" opacity="0.85" rx="3"/>
<rect x="170" y="140" width="48" height="140" fill="#3fb950" opacity="0.85" rx="3"/>
<rect x="225" y="120" width="48" height="160" fill="#3fb950" opacity="0.85" rx="3"/>
<rect x="280" y="105" width="48" height="175" fill="#3fb950" opacity="0.85" rx="3"/>
<text x="84" y="298" text-anchor="middle" fill="#8b949e">$0.5</text>
<text x="139" y="298" text-anchor="middle" fill="#8b949e">$0.6</text>
<text x="194" y="298" text-anchor="middle" fill="#8b949e">$0.7</text>
<text x="249" y="298" text-anchor="middle" fill="#8b949e">$0.8</text>
<text x="304" y="298" text-anchor="middle" fill="#8b949e">$0.9</text>
</g>
<!-- Active -->
<rect x="358" y="100" width="44" height="180" fill="#f7b955" opacity="0.9" rx="3"/>
<text x="380" y="298" text-anchor="middle" fill="#f7b955" font-weight="700">$1.0</text>
<!-- X bins (right, token) -->
<g>
<rect x="430" y="105" width="48" height="175" fill="#bc8cff" opacity="0.85" rx="3"/>
<rect x="485" y="120" width="48" height="160" fill="#bc8cff" opacity="0.85" rx="3"/>
<rect x="540" y="140" width="48" height="140" fill="#bc8cff" opacity="0.85" rx="3"/>
<rect x="595" y="160" width="48" height="120" fill="#bc8cff" opacity="0.85" rx="3"/>
<rect x="650" y="180" width="48" height="100" fill="#bc8cff" opacity="0.85" rx="3"/>
<text x="454" y="298" text-anchor="middle" fill="#8b949e">$1.1</text>
<text x="509" y="298" text-anchor="middle" fill="#8b949e">$1.2</text>
<text x="564" y="298" text-anchor="middle" fill="#8b949e">$1.3</text>
<text x="619" y="298" text-anchor="middle" fill="#8b949e">$1.4</text>
<text x="674" y="298" text-anchor="middle" fill="#8b949e">$1.5</text>
</g>
</g>
<!-- Labels -->
<text x="194" y="80" text-anchor="middle" fill="#3fb950" font-size="13" font-weight="700">下方 bins</text>
<text x="194" y="98" text-anchor="middle" fill="#3fb950" font-size="11">装 USDC(限价买单)</text>
<text x="564" y="80" text-anchor="middle" fill="#bc8cff" font-size="13" font-weight="700">上方 bins</text>
<text x="564" y="98" text-anchor="middle" fill="#bc8cff" font-size="11">装 代币 X(限价卖单)</text>
</svg>
<figcaption>DLMM 池子结构:active bin 把价格分成左右两半。左边的 bin 装 USDC,右边的装代币。</figcaption>
</figure>
<div class="callout">
<strong>关键三点:</strong>
<ol style="margin:8px 0 0; padding-left: 20px;">
<li>每个 bin 对应一个<strong>固定</strong>的兑换价(创建时算好的常数)。</li>
<li>active bin <strong>下方</strong>的 bins 装 Y(USDC),是"低价等买 X 的限价买单"。</li>
<li>active bin <strong>上方</strong>的 bins 装 X(代币),是"高价等卖出的限价卖单"。</li>
</ol>
</div>
<h2>3. 为什么 bin 会"老价格存在"?</h2>
<p>这是这次事件最反直觉的地方。<strong>DLMM 池子的价格不会自动跟着外部市场走</strong>——它只在<em>有人在本池子做 swap</em>的时候才移动 active bin。</p>
<figure>
<svg viewBox="0 0 760 360" xmlns="http://www.w3.org/2000/svg">
<rect width="760" height="360" fill="url(#bg1)" rx="10"/>
<!-- Time axis -->
<line x1="60" y1="320" x2="700" y2="320" stroke="#30363d" stroke-width="1"/>
<text x="60" y="345" fill="#8b949e" font-size="11">Day 0</text>
<text x="380" y="345" fill="#8b949e" font-size="11">Day 1 ~ 300(无人交易)</text>
<text x="700" y="345" text-anchor="end" fill="#8b949e" font-size="11">Day 300(被发现)</text>
<!-- Raydium price line - rising -->
<text x="60" y="48" fill="#3fb950" font-size="12" font-weight="700">Raydium 真实市价</text>
<path d="M60,290 Q200,280 300,200 T700,80" stroke="#3fb950" stroke-width="2.5" fill="none"/>
<circle cx="60" cy="290" r="4" fill="#3fb950"/>
<circle cx="700" cy="80" r="5" fill="#3fb950"/>
<text x="80" y="285" fill="#3fb950" font-size="11">$0.001</text>
<text x="688" y="74" text-anchor="end" fill="#3fb950" font-size="11" font-weight="700">$1.00 ↑↑↑</text>
<!-- Meteora price line - flat -->
<text x="60" y="200" fill="#f85149" font-size="12" font-weight="700">Meteora 池子 active bin</text>
<line x1="60" y1="290" x2="640" y2="290" stroke="#f85149" stroke-width="2.5" stroke-dasharray="6 4"/>
<circle cx="60" cy="290" r="4" fill="#f85149"/>
<circle cx="640" cy="290" r="4" fill="#f85149"/>
<text x="380" y="282" text-anchor="middle" fill="#f85149" font-size="11">10 个月一动不动 · 一直停在 $0.001</text>
<!-- Gap arrow -->
<line x1="660" y1="80" x2="660" y2="280" stroke="#f7b955" stroke-width="1.5" stroke-dasharray="3 3"/>
<line x1="650" y1="80" x2="670" y2="80" stroke="#f7b955" stroke-width="1.5"/>
<line x1="650" y1="280" x2="670" y2="280" stroke="#f7b955" stroke-width="1.5"/>
<text x="678" y="180" fill="#f7b955" font-size="13" font-weight="700">价差</text>
<text x="678" y="198" fill="#f7b955" font-size="11">1000×</text>
</svg>
<figcaption>外部市场涨了 1000 倍,Meteora 池子的 active bin 却没移动半步——因为没人来这里 swap。</figcaption>
</figure>
<p>所以"代币涨了为什么 bin 还能低价存在"的答案是:</p>
<div class="callout warn">
<strong>Solana 链上没有"全局市场价"。</strong>每个 DEX 池子都是独立的小宇宙。Raydium 的价格涨到 $1 跟 Meteora 这个池子毫无机械联系——除非有套利机器人主动来"对齐"它。在它被发现之前,那批挂在 $0.001、$0.0011、$0.005、$0.01 ... 的 X 代币就一直**安静地坐在那儿等买家**。
</div>
<h2>4. 攻击是怎么发生的(图解)</h2>
<p>为了讲清楚机器人具体在干什么,下面把那笔 transaction 拆成 4 个画面:</p>
<figure>
<svg viewBox="0 0 760 480" xmlns="http://www.w3.org/2000/svg">
<rect width="760" height="480" fill="url(#bg1)" rx="10"/>
<!-- Frame 1 -->
<text x="20" y="34" fill="#f7b955" font-size="13" font-weight="700">① 池子初始状态:active bin 在 $0.001,上方塞满便宜 X 代币</text>
<g transform="translate(40, 50)">
<rect x="0" y="0" width="40" height="60" fill="#f7b955" rx="2"/>
<rect x="48" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/>
<rect x="96" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/>
<rect x="144" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/>
<rect x="192" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/>
<rect x="240" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/>
<rect x="288" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/>
<rect x="336" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/>
<text x="20" y="78" text-anchor="middle" fill="#f7b955" font-size="10">$0.001</text>
<text x="68" y="78" text-anchor="middle" fill="#8b949e" font-size="10">$0.002</text>
<text x="116" y="78" text-anchor="middle" fill="#8b949e" font-size="10">$0.005</text>
<text x="164" y="78" text-anchor="middle" fill="#8b949e" font-size="10">$0.01</text>
<text x="212" y="78" text-anchor="middle" fill="#8b949e" font-size="10">$0.05</text>
<text x="260" y="78" text-anchor="middle" fill="#8b949e" font-size="10">$0.2</text>
<text x="308" y="78" text-anchor="middle" fill="#8b949e" font-size="10">$0.5</text>
<text x="356" y="78" text-anchor="middle" fill="#8b949e" font-size="10">$1.0</text>
<text x="450" y="38" fill="#bc8cff" font-size="11">每个紫格里都有大量代币 X,标价远低于市价</text>
</g>
<!-- Frame 2 -->
<text x="20" y="148" fill="#f7b955" font-size="13" font-weight="700">② 机器人灌入 USDC,吞掉第一格 bin(按 $0.001 买)</text>
<g transform="translate(40, 164)">
<rect x="0" y="0" width="40" height="60" fill="#3fb950" opacity="0.5" stroke="#3fb950" stroke-width="2" rx="2"/>
<rect x="48" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/>
<rect x="96" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/>
<rect x="144" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/>
<rect x="192" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/>
<rect x="240" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/>
<rect x="288" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/>
<rect x="336" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/>
<text x="20" y="78" text-anchor="middle" fill="#3fb950" font-size="10" font-weight="700">已吃完</text>
<text x="450" y="38" fill="#3fb950" font-size="11">代币 X 进入机器人钱包,bin #1 变成 USDC</text>
</g>
<!-- Frame 3 -->
<text x="20" y="262" fill="#f7b955" font-size="13" font-weight="700">③ active bin 推到下一格,继续吃(按 $0.002, $0.005, $0.01... 一路买上去)</text>
<g transform="translate(40, 278)">
<rect x="0" y="0" width="40" height="60" fill="#3fb950" opacity="0.5" stroke="#3fb950" stroke-width="1" rx="2"/>
<rect x="48" y="0" width="40" height="60" fill="#3fb950" opacity="0.5" stroke="#3fb950" stroke-width="1" rx="2"/>
<rect x="96" y="0" width="40" height="60" fill="#3fb950" opacity="0.5" stroke="#3fb950" stroke-width="1" rx="2"/>
<rect x="144" y="0" width="40" height="60" fill="#3fb950" opacity="0.5" stroke="#3fb950" stroke-width="1" rx="2"/>
<rect x="192" y="0" width="40" height="60" fill="#3fb950" opacity="0.5" stroke="#3fb950" stroke-width="1" rx="2"/>
<rect x="240" y="0" width="40" height="60" fill="#f7b955" rx="2"/>
<rect x="288" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/>
<rect x="336" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/>
<text x="450" y="38" fill="#f7b955" font-size="11">active bin 一格格往上推,直到逼近真实市价 $1.0</text>
</g>
<!-- Frame 4 -->
<text x="20" y="378" fill="#f7b955" font-size="13" font-weight="700">④ 同一笔 transaction 里把吃到的 X 全部发到 Raydium 按 $1.0 卖出</text>
<g transform="translate(40, 394)">
<rect x="0" y="0" width="80" height="60" fill="#161b22" stroke="#bc8cff" stroke-width="1.5" rx="6"/>
<text x="40" y="26" text-anchor="middle" fill="#bc8cff" font-size="11" font-weight="700">Meteora</text>
<text x="40" y="44" text-anchor="middle" fill="#8b949e" font-size="10">买 X · 极便宜</text>
<text x="160" y="34" fill="#f7b955" font-size="20">→</text>
<rect x="200" y="0" width="80" height="60" fill="#161b22" stroke="#3fb950" stroke-width="1.5" rx="6"/>
<text x="240" y="26" text-anchor="middle" fill="#3fb950" font-size="11" font-weight="700">Raydium</text>
<text x="240" y="44" text-anchor="middle" fill="#8b949e" font-size="10">卖 X · 市价</text>
<text x="320" y="34" fill="#f7b955" font-size="20">→</text>
<rect x="360" y="0" width="120" height="60" fill="#161b22" stroke="#f7b955" stroke-width="1.5" rx="6"/>
<text x="420" y="26" text-anchor="middle" fill="#f7b955" font-size="11" font-weight="700">机器人钱包</text>
<text x="420" y="44" text-anchor="middle" fill="#f7b955" font-size="10" font-weight="700">+ $400K USDC</text>
<text x="540" y="38" fill="#8b949e" font-size="11">所有指令打包进 1 笔 tx,</text>
<text x="540" y="54" fill="#8b949e" font-size="11">失败回滚 → 永远不会亏</text>
</g>
</svg>
<figcaption>套利的整个动作:低价吃光 → 市价卖出 → 同一笔交易原子完成。</figcaption>
</figure>
<h3>事件时间线</h3>
<table>
<thead>
<tr><th>时刻</th><th>角色</th><th>动作</th><th>结果</th></tr>
</thead>
<tbody>
<tr><td>T+0s</td><td>鲸鱼</td><td>大额 swap,第一次"打开"这个被遗忘的池子</td><td>active bin 开始移动</td></tr>
<tr><td>T+0.4s</td><td>Bot 1</td><td>监听器触发,发起反向套利</td><td>$0.16 成本 → $403K 盈利</td></tr>
<tr><td>T+1.2s</td><td>Bot 2</td><td>下一个 block 抢落地</td><td>$0.23 成本 → $696K 盈利</td></tr>
<tr><td>T+0~150s</td><td>Bot 3-6</td><td>剩余 bin 慢慢被扫</td><td>累计接近 $3M</td></tr>
<tr><td>T+150s</td><td>—</td><td>active bin 已逼近 Raydium 市价</td><td>套利窗口关闭</td></tr>
</tbody>
</table>
<h2>5. 技术实现骨架(如果你想做)</h2>
<ol class="steps">
<li><strong>扫描全网 Meteora DLMM 池找"stale"标的。</strong>拉所有 DLMM 池的 PDA 账户,用 SDK 读 active bin 价格,跟 Jupiter / Birdeye 拿到的市价对比。差 5-10% 以上的进候选池。这一步可以离线一天扫一次。</li>
<li><strong>实时盯盘候选池。</strong>用 Helius / Triton 的 Geyser gRPC 或 <code>accountSubscribe</code> WebSocket 订阅这些池子账户,状态一变就 push 给你(延迟 50-200ms)。</li>
<li><strong>盈亏模拟。</strong>调用 <code>@meteora-ag/dlmm</code> 模拟从这个池子买 X、再到 Raydium 卖的总收益。扣掉两边手续费、Solana 优先费、Jito tip 后还赚才发交易。</li>
<li><strong>原子 transaction。</strong>把"Meteora 买 + Raydium 卖"两条 swap 指令打到同一笔 tx 里,每个指令都设 <code>min_out_amount</code>,价格滑了整笔回滚。账户多塞不下时用 Address Lookup Table (LUT) 压缩。</li>
<li><strong>抢落地。</strong>普通 RPC 抢不过别人。主流做法:Jito bundle(带 tip)+ Helius Sender / Triton Cascade 同时发出去,哪个先 land 算哪个。tip 大小是 bot 之间最常调的旋钮。</li>
</ol>
<div class="callout">
<strong>提示:</strong>这种机会真正的瓶颈不在策略本身,而在<strong>抢速度</strong>和<strong>风险控制</strong>。一旦池子被发现,每个 block 都有 5-20 个机器人在抢同一笔利润。原子 + Jito + 充足的 priority fee 是最低门槛。
</div>
<h2>6. 一句话总结</h2>
<div class="quote">
<strong>Bin 不会自动更新价格</strong>,DLMM 池子只在被交易时才移动 active bin。LP 创建池子后 10 个月不维护,外部市场暴涨 1000 倍——池子里那些"高于原始 active bin 但远低于市场价"的 bins 就成了静悄悄挂着的「便宜代币堆」,等着第一个发现它的套利机器人一口气全吃光。
</div>
<p>这就是 stale pool / stale LP arbitrage。错不在 Meteora 协议,而在 LP 自己。这种漏洞跟 Uniswap v3 在以太坊上遇到的"untracked LP"问题完全一样,只是 Solana 上池子多、LP 散、被发现的速度极快——这次连 1 秒都没撑住。</p>
<footer>
<p>资料来源:
<a href="https://x.com/anshu_code/status/2051888998671581324" target="_blank">@anshu_code 主推</a> ·
<a href="https://x.com/anshu_code/status/2051392982327468463" target="_blank">前一条引用</a> ·
Meteora DLMM 文档 · Jupiter / Helius 公开技术资料
</p>
<p>本文为根据公开链上数据和推文内容做的技术拆解,不构成投资建议;过期池套利涉及极高的速度竞争和资金风险,复刻前请自行评估。</p>
</footer>
</div>
</body>
</html>
| 1 | <!DOCTYPE html> |
| 2 | <html lang="zh-CN"> |
| 3 | <head> |
| 4 | <meta charset="UTF-8" /> |
| 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| 6 | <title>Solana Meteora 过期池套利拆解:299 万美金消失的 150 秒</title> |
| 7 | <style> |
| 8 | :root { |
| 9 | --bg: #0d1117; |
| 10 | --panel: #161b22; |
| 11 | --panel2: #1c2330; |
| 12 | --border: #30363d; |
| 13 | --text: #e6edf3; |
| 14 | --text-dim: #8b949e; |
| 15 | --accent: #f7b955; |
| 16 | --accent2: #58a6ff; |
| 17 | --green: #3fb950; |
| 18 | --red: #f85149; |
| 19 | --purple: #bc8cff; |
| 20 | } |
| 21 | * { box-sizing: border-box; } |
| 22 | html, body { |
| 23 | margin: 0; padding: 0; |
| 24 | background: var(--bg); |
| 25 | color: var(--text); |
| 26 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", |
| 27 | "Hiragino Sans GB", "Microsoft YaHei", sans-serif; |
| 28 | line-height: 1.7; |
| 29 | } |
| 30 | .wrap { |
| 31 | max-width: 920px; |
| 32 | margin: 0 auto; |
| 33 | padding: 48px 28px 96px; |
| 34 | } |
| 35 | header { |
| 36 | border-bottom: 1px solid var(--border); |
| 37 | padding-bottom: 28px; |
| 38 | margin-bottom: 36px; |
| 39 | } |
| 40 | .tag { |
| 41 | display: inline-block; |
| 42 | background: rgba(247, 185, 85, 0.12); |
| 43 | color: var(--accent); |
| 44 | padding: 4px 12px; |
| 45 | border-radius: 999px; |
| 46 | font-size: 12px; |
| 47 | letter-spacing: 0.04em; |
| 48 | text-transform: uppercase; |
| 49 | margin-bottom: 16px; |
| 50 | border: 1px solid rgba(247, 185, 85, 0.3); |
| 51 | } |
| 52 | h1 { |
| 53 | font-size: 34px; |
| 54 | line-height: 1.25; |
| 55 | margin: 0 0 12px; |
| 56 | font-weight: 700; |
| 57 | letter-spacing: -0.01em; |
| 58 | } |
| 59 | .subtitle { |
| 60 | color: var(--text-dim); |
| 61 | font-size: 16px; |
| 62 | margin: 0; |
| 63 | } |
| 64 | h2 { |
| 65 | font-size: 22px; |
| 66 | margin: 56px 0 14px; |
| 67 | padding-left: 12px; |
| 68 | border-left: 4px solid var(--accent); |
| 69 | } |
| 70 | h3 { |
| 71 | font-size: 17px; |
| 72 | margin: 28px 0 10px; |
| 73 | color: var(--accent2); |
| 74 | } |
| 75 | p { margin: 12px 0; } |
| 76 | code { |
| 77 | background: var(--panel2); |
| 78 | border: 1px solid var(--border); |
| 79 | padding: 1px 6px; |
| 80 | border-radius: 4px; |
| 81 | font-size: 13.5px; |
| 82 | color: var(--accent); |
| 83 | font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; |
| 84 | } |
| 85 | .stats { |
| 86 | display: grid; |
| 87 | grid-template-columns: repeat(4, 1fr); |
| 88 | gap: 12px; |
| 89 | margin: 28px 0; |
| 90 | } |
| 91 | .stat { |
| 92 | background: var(--panel); |
| 93 | border: 1px solid var(--border); |
| 94 | border-radius: 10px; |
| 95 | padding: 18px 16px; |
| 96 | text-align: center; |
| 97 | } |
| 98 | .stat .num { |
| 99 | font-size: 26px; |
| 100 | font-weight: 700; |
| 101 | color: var(--accent); |
| 102 | margin-bottom: 4px; |
| 103 | font-variant-numeric: tabular-nums; |
| 104 | } |
| 105 | .stat .lbl { |
| 106 | color: var(--text-dim); |
| 107 | font-size: 12.5px; |
| 108 | } |
| 109 | .quote { |
| 110 | background: var(--panel); |
| 111 | border-left: 3px solid var(--accent2); |
| 112 | padding: 16px 20px; |
| 113 | border-radius: 0 8px 8px 0; |
| 114 | color: var(--text); |
| 115 | font-size: 15px; |
| 116 | margin: 18px 0; |
| 117 | } |
| 118 | .quote .src { |
| 119 | display: block; |
| 120 | margin-top: 8px; |
| 121 | color: var(--text-dim); |
| 122 | font-size: 12.5px; |
| 123 | } |
| 124 | .panel { |
| 125 | background: var(--panel); |
| 126 | border: 1px solid var(--border); |
| 127 | border-radius: 12px; |
| 128 | padding: 22px 24px; |
| 129 | margin: 22px 0; |
| 130 | } |
| 131 | figure { |
| 132 | margin: 22px 0; |
| 133 | background: var(--panel); |
| 134 | border: 1px solid var(--border); |
| 135 | border-radius: 12px; |
| 136 | padding: 22px 18px 14px; |
| 137 | } |
| 138 | figcaption { |
| 139 | color: var(--text-dim); |
| 140 | font-size: 13px; |
| 141 | text-align: center; |
| 142 | margin-top: 10px; |
| 143 | } |
| 144 | svg { display: block; max-width: 100%; height: auto; margin: 0 auto; } |
| 145 | |
| 146 | table { |
| 147 | width: 100%; |
| 148 | border-collapse: collapse; |
| 149 | margin: 18px 0; |
| 150 | font-size: 14px; |
| 151 | } |
| 152 | th, td { |
| 153 | padding: 10px 12px; |
| 154 | border-bottom: 1px solid var(--border); |
| 155 | text-align: left; |
| 156 | } |
| 157 | th { |
| 158 | background: var(--panel2); |
| 159 | color: var(--text-dim); |
| 160 | font-weight: 600; |
| 161 | font-size: 13px; |
| 162 | text-transform: uppercase; |
| 163 | letter-spacing: 0.03em; |
| 164 | } |
| 165 | ol.steps { |
| 166 | counter-reset: s; |
| 167 | list-style: none; |
| 168 | padding: 0; |
| 169 | } |
| 170 | ol.steps > li { |
| 171 | counter-increment: s; |
| 172 | position: relative; |
| 173 | padding: 14px 18px 14px 56px; |
| 174 | margin: 10px 0; |
| 175 | background: var(--panel); |
| 176 | border: 1px solid var(--border); |
| 177 | border-radius: 10px; |
| 178 | } |
| 179 | ol.steps > li::before { |
| 180 | content: counter(s); |
| 181 | position: absolute; |
| 182 | left: 16px; |
| 183 | top: 14px; |
| 184 | width: 28px; |
| 185 | height: 28px; |
| 186 | border-radius: 50%; |
| 187 | background: var(--accent); |
| 188 | color: #1d1300; |
| 189 | font-weight: 700; |
| 190 | text-align: center; |
| 191 | line-height: 28px; |
| 192 | font-size: 14px; |
| 193 | } |
| 194 | .callout { |
| 195 | background: rgba(88, 166, 255, 0.08); |
| 196 | border: 1px solid rgba(88, 166, 255, 0.3); |
| 197 | border-radius: 10px; |
| 198 | padding: 14px 18px; |
| 199 | margin: 18px 0; |
| 200 | color: var(--text); |
| 201 | font-size: 14.5px; |
| 202 | } |
| 203 | .callout.warn { |
| 204 | background: rgba(248, 81, 73, 0.08); |
| 205 | border-color: rgba(248, 81, 73, 0.3); |
| 206 | } |
| 207 | .callout strong { color: var(--accent2); } |
| 208 | .callout.warn strong { color: var(--red); } |
| 209 | footer { |
| 210 | margin-top: 64px; |
| 211 | padding-top: 24px; |
| 212 | border-top: 1px solid var(--border); |
| 213 | color: var(--text-dim); |
| 214 | font-size: 13px; |
| 215 | } |
| 216 | footer a { color: var(--accent2); text-decoration: none; } |
| 217 | footer a:hover { text-decoration: underline; } |
| 218 | |
| 219 | @media (max-width: 640px) { |
| 220 | .wrap { padding: 28px 16px 64px; } |
| 221 | h1 { font-size: 26px; } |
| 222 | .stats { grid-template-columns: repeat(2, 1fr); } |
| 223 | } |
| 224 | </style> |
| 225 | </head> |
| 226 | <body> |
| 227 | <div class="wrap"> |
| 228 | |
| 229 | <header> |
| 230 | <span class="tag">Solana · MEV · DEX 套利</span> |
| 231 | <h1>299 万美金,150 秒蒸发<br/>—— Meteora 过期池套利完整拆解</h1> |
| 232 | <p class="subtitle">用图解搞懂:DLMM 的 bin 是什么,为什么旧 LP 池会变成"白送代币的提款机",以及套利机器人是怎么把它们一口气吞掉的。</p> |
| 233 | </header> |
| 234 | |
| 235 | <section> |
| 236 | <div class="stats"> |
| 237 | <div class="stat"><div class="num">$2.99M</div><div class="lbl">被抽走金额</div></div> |
| 238 | <div class="stat"><div class="num">150s</div><div class="lbl">总耗时</div></div> |
| 239 | <div class="stat"><div class="num">6 + 1</div><div class="lbl">机器人 + 鲸鱼</div></div> |
| 240 | <div class="stat"><div class="num">10 个月</div><div class="lbl">池子无人维护</div></div> |
| 241 | </div> |
| 242 | |
| 243 | <div class="quote"> |
| 244 | "$2,993,502 drained from a single Solana pool in 150 seconds. 1 whale. 6 bots. 9 transactions. 1 misconfigured Meteora pool that sat for 10 months." |
| 245 | <span class="src">—— @anshu_code, X / 2026-05-06</span> |
| 246 | </div> |
| 247 | |
| 248 | <p>这条推文讲的,是 Solana 链上一个被忽略了 10 个月的 <strong>Meteora DLMM 流动性池</strong>,在 150 秒内被 6 个套利机器人和一个大户合力"洗"了一遍,搬走 299 万美金。本文用图把它拆开讲清楚。</p> |
| 249 | </section> |
| 250 | |
| 251 | <h2>1. 它属于哪一类套利?</h2> |
| 252 | <p>这种玩法的术语叫 <strong>Stale Pool Arbitrage(过期池套利)</strong>。靶子是那些 <em>LP 挂上去就不再维护</em> 的集中流动性池——挂的还是几个月前的"老价格",外部市场早已涨飞,池子却毫无觉察。</p> |
| 253 | |
| 254 | <figure> |
| 255 | <svg viewBox="0 0 760 280" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="过期池套利示意"> |
| 256 | <defs> |
| 257 | <linearGradient id="bg1" x1="0" x2="0" y1="0" y2="1"> |
| 258 | <stop offset="0" stop-color="#1c2330"/> |
| 259 | <stop offset="1" stop-color="#161b22"/> |
| 260 | </linearGradient> |
| 261 | <marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto"> |
| 262 | <path d="M0,0 L8,3 L0,6 z" fill="#f7b955"/> |
| 263 | </marker> |
| 264 | </defs> |
| 265 | <rect width="760" height="280" fill="url(#bg1)" rx="10"/> |
| 266 | |
| 267 | <!-- Raydium box --> |
| 268 | <rect x="40" y="60" width="240" height="160" fill="#0d1117" stroke="#3fb950" stroke-width="1.5" rx="10"/> |
| 269 | <text x="160" y="90" text-anchor="middle" fill="#3fb950" font-size="15" font-weight="700">Raydium / 真实市场</text> |
| 270 | <text x="160" y="135" text-anchor="middle" fill="#e6edf3" font-size="13">代币 X 已涨到</text> |
| 271 | <text x="160" y="170" text-anchor="middle" fill="#3fb950" font-size="32" font-weight="700">$1.00</text> |
| 272 | <text x="160" y="195" text-anchor="middle" fill="#8b949e" font-size="11">活跃交易 · 价格随市场实时更新</text> |
| 273 | |
| 274 | <!-- Meteora box --> |
| 275 | <rect x="480" y="60" width="240" height="160" fill="#0d1117" stroke="#f85149" stroke-width="1.5" rx="10"/> |
| 276 | <text x="600" y="90" text-anchor="middle" fill="#f85149" font-size="15" font-weight="700">Meteora 旧池子</text> |
| 277 | <text x="600" y="135" text-anchor="middle" fill="#e6edf3" font-size="13">10 个月没人交易</text> |
| 278 | <text x="600" y="170" text-anchor="middle" fill="#f85149" font-size="32" font-weight="700">$0.001</text> |
| 279 | <text x="600" y="195" text-anchor="middle" fill="#8b949e" font-size="11">价格停在 LP 创建时的水平</text> |
| 280 | |
| 281 | <!-- Bridge / arrow --> |
| 282 | <text x="380" y="130" text-anchor="middle" fill="#f7b955" font-size="13" font-weight="700">价差 1000×</text> |
| 283 | <line x1="290" y1="150" x2="470" y2="150" stroke="#f7b955" stroke-width="2" marker-end="url(#arrow)"/> |
| 284 | <text x="380" y="180" text-anchor="middle" fill="#8b949e" font-size="11">套利机器人在这里下手</text> |
| 285 | </svg> |
| 286 | <figcaption>同一种代币,两个池子,价格差 1000 倍。差距在 LP 不维护时会一直存在。</figcaption> |
| 287 | </figure> |
| 288 | |
| 289 | <h2>2. 先理解 DLMM:什么是 bin?</h2> |
| 290 | <p>Meteora DLMM(Dynamic Liquidity Market Maker)跟 Uniswap v3 思路一样:把价格切成一格一格的"档位",每一档叫 <strong>bin</strong>。LP 不是在一条曲线上放钱,而是把代币塞进具体某几个 bin 里。</p> |
| 291 | |
| 292 | <figure> |
| 293 | <svg viewBox="0 0 760 320" xmlns="http://www.w3.org/2000/svg"> |
| 294 | <rect width="760" height="320" fill="url(#bg1)" rx="10"/> |
| 295 | |
| 296 | <!-- Active bin marker --> |
| 297 | <line x1="380" y1="40" x2="380" y2="280" stroke="#f7b955" stroke-width="1" stroke-dasharray="4 4"/> |
| 298 | <text x="380" y="32" text-anchor="middle" fill="#f7b955" font-size="13" font-weight="700">↓ Active Bin(当前价)</text> |
| 299 | |
| 300 | <!-- Bins --> |
| 301 | <g font-family="ui-monospace, monospace" font-size="11"> |
| 302 | <!-- Y bins (left, USDC) --> |
| 303 | <g> |
| 304 | <rect x="60" y="180" width="48" height="100" fill="#3fb950" opacity="0.85" rx="3"/> |
| 305 | <rect x="115" y="160" width="48" height="120" fill="#3fb950" opacity="0.85" rx="3"/> |
| 306 | <rect x="170" y="140" width="48" height="140" fill="#3fb950" opacity="0.85" rx="3"/> |
| 307 | <rect x="225" y="120" width="48" height="160" fill="#3fb950" opacity="0.85" rx="3"/> |
| 308 | <rect x="280" y="105" width="48" height="175" fill="#3fb950" opacity="0.85" rx="3"/> |
| 309 | <text x="84" y="298" text-anchor="middle" fill="#8b949e">$0.5</text> |
| 310 | <text x="139" y="298" text-anchor="middle" fill="#8b949e">$0.6</text> |
| 311 | <text x="194" y="298" text-anchor="middle" fill="#8b949e">$0.7</text> |
| 312 | <text x="249" y="298" text-anchor="middle" fill="#8b949e">$0.8</text> |
| 313 | <text x="304" y="298" text-anchor="middle" fill="#8b949e">$0.9</text> |
| 314 | </g> |
| 315 | <!-- Active --> |
| 316 | <rect x="358" y="100" width="44" height="180" fill="#f7b955" opacity="0.9" rx="3"/> |
| 317 | <text x="380" y="298" text-anchor="middle" fill="#f7b955" font-weight="700">$1.0</text> |
| 318 | |
| 319 | <!-- X bins (right, token) --> |
| 320 | <g> |
| 321 | <rect x="430" y="105" width="48" height="175" fill="#bc8cff" opacity="0.85" rx="3"/> |
| 322 | <rect x="485" y="120" width="48" height="160" fill="#bc8cff" opacity="0.85" rx="3"/> |
| 323 | <rect x="540" y="140" width="48" height="140" fill="#bc8cff" opacity="0.85" rx="3"/> |
| 324 | <rect x="595" y="160" width="48" height="120" fill="#bc8cff" opacity="0.85" rx="3"/> |
| 325 | <rect x="650" y="180" width="48" height="100" fill="#bc8cff" opacity="0.85" rx="3"/> |
| 326 | <text x="454" y="298" text-anchor="middle" fill="#8b949e">$1.1</text> |
| 327 | <text x="509" y="298" text-anchor="middle" fill="#8b949e">$1.2</text> |
| 328 | <text x="564" y="298" text-anchor="middle" fill="#8b949e">$1.3</text> |
| 329 | <text x="619" y="298" text-anchor="middle" fill="#8b949e">$1.4</text> |
| 330 | <text x="674" y="298" text-anchor="middle" fill="#8b949e">$1.5</text> |
| 331 | </g> |
| 332 | </g> |
| 333 | |
| 334 | <!-- Labels --> |
| 335 | <text x="194" y="80" text-anchor="middle" fill="#3fb950" font-size="13" font-weight="700">下方 bins</text> |
| 336 | <text x="194" y="98" text-anchor="middle" fill="#3fb950" font-size="11">装 USDC(限价买单)</text> |
| 337 | |
| 338 | <text x="564" y="80" text-anchor="middle" fill="#bc8cff" font-size="13" font-weight="700">上方 bins</text> |
| 339 | <text x="564" y="98" text-anchor="middle" fill="#bc8cff" font-size="11">装 代币 X(限价卖单)</text> |
| 340 | </svg> |
| 341 | <figcaption>DLMM 池子结构:active bin 把价格分成左右两半。左边的 bin 装 USDC,右边的装代币。</figcaption> |
| 342 | </figure> |
| 343 | |
| 344 | <div class="callout"> |
| 345 | <strong>关键三点:</strong> |
| 346 | <ol style="margin:8px 0 0; padding-left: 20px;"> |
| 347 | <li>每个 bin 对应一个<strong>固定</strong>的兑换价(创建时算好的常数)。</li> |
| 348 | <li>active bin <strong>下方</strong>的 bins 装 Y(USDC),是"低价等买 X 的限价买单"。</li> |
| 349 | <li>active bin <strong>上方</strong>的 bins 装 X(代币),是"高价等卖出的限价卖单"。</li> |
| 350 | </ol> |
| 351 | </div> |
| 352 | |
| 353 | <h2>3. 为什么 bin 会"老价格存在"?</h2> |
| 354 | <p>这是这次事件最反直觉的地方。<strong>DLMM 池子的价格不会自动跟着外部市场走</strong>——它只在<em>有人在本池子做 swap</em>的时候才移动 active bin。</p> |
| 355 | |
| 356 | <figure> |
| 357 | <svg viewBox="0 0 760 360" xmlns="http://www.w3.org/2000/svg"> |
| 358 | <rect width="760" height="360" fill="url(#bg1)" rx="10"/> |
| 359 | |
| 360 | <!-- Time axis --> |
| 361 | <line x1="60" y1="320" x2="700" y2="320" stroke="#30363d" stroke-width="1"/> |
| 362 | <text x="60" y="345" fill="#8b949e" font-size="11">Day 0</text> |
| 363 | <text x="380" y="345" fill="#8b949e" font-size="11">Day 1 ~ 300(无人交易)</text> |
| 364 | <text x="700" y="345" text-anchor="end" fill="#8b949e" font-size="11">Day 300(被发现)</text> |
| 365 | |
| 366 | <!-- Raydium price line - rising --> |
| 367 | <text x="60" y="48" fill="#3fb950" font-size="12" font-weight="700">Raydium 真实市价</text> |
| 368 | <path d="M60,290 Q200,280 300,200 T700,80" stroke="#3fb950" stroke-width="2.5" fill="none"/> |
| 369 | <circle cx="60" cy="290" r="4" fill="#3fb950"/> |
| 370 | <circle cx="700" cy="80" r="5" fill="#3fb950"/> |
| 371 | <text x="80" y="285" fill="#3fb950" font-size="11">$0.001</text> |
| 372 | <text x="688" y="74" text-anchor="end" fill="#3fb950" font-size="11" font-weight="700">$1.00 ↑↑↑</text> |
| 373 | |
| 374 | <!-- Meteora price line - flat --> |
| 375 | <text x="60" y="200" fill="#f85149" font-size="12" font-weight="700">Meteora 池子 active bin</text> |
| 376 | <line x1="60" y1="290" x2="640" y2="290" stroke="#f85149" stroke-width="2.5" stroke-dasharray="6 4"/> |
| 377 | <circle cx="60" cy="290" r="4" fill="#f85149"/> |
| 378 | <circle cx="640" cy="290" r="4" fill="#f85149"/> |
| 379 | <text x="380" y="282" text-anchor="middle" fill="#f85149" font-size="11">10 个月一动不动 · 一直停在 $0.001</text> |
| 380 | |
| 381 | <!-- Gap arrow --> |
| 382 | <line x1="660" y1="80" x2="660" y2="280" stroke="#f7b955" stroke-width="1.5" stroke-dasharray="3 3"/> |
| 383 | <line x1="650" y1="80" x2="670" y2="80" stroke="#f7b955" stroke-width="1.5"/> |
| 384 | <line x1="650" y1="280" x2="670" y2="280" stroke="#f7b955" stroke-width="1.5"/> |
| 385 | <text x="678" y="180" fill="#f7b955" font-size="13" font-weight="700">价差</text> |
| 386 | <text x="678" y="198" fill="#f7b955" font-size="11">1000×</text> |
| 387 | </svg> |
| 388 | <figcaption>外部市场涨了 1000 倍,Meteora 池子的 active bin 却没移动半步——因为没人来这里 swap。</figcaption> |
| 389 | </figure> |
| 390 | |
| 391 | <p>所以"代币涨了为什么 bin 还能低价存在"的答案是:</p> |
| 392 | <div class="callout warn"> |
| 393 | <strong>Solana 链上没有"全局市场价"。</strong>每个 DEX 池子都是独立的小宇宙。Raydium 的价格涨到 $1 跟 Meteora 这个池子毫无机械联系——除非有套利机器人主动来"对齐"它。在它被发现之前,那批挂在 $0.001、$0.0011、$0.005、$0.01 ... 的 X 代币就一直**安静地坐在那儿等买家**。 |
| 394 | </div> |
| 395 | |
| 396 | <h2>4. 攻击是怎么发生的(图解)</h2> |
| 397 | <p>为了讲清楚机器人具体在干什么,下面把那笔 transaction 拆成 4 个画面:</p> |
| 398 | |
| 399 | <figure> |
| 400 | <svg viewBox="0 0 760 480" xmlns="http://www.w3.org/2000/svg"> |
| 401 | <rect width="760" height="480" fill="url(#bg1)" rx="10"/> |
| 402 | |
| 403 | <!-- Frame 1 --> |
| 404 | <text x="20" y="34" fill="#f7b955" font-size="13" font-weight="700">① 池子初始状态:active bin 在 $0.001,上方塞满便宜 X 代币</text> |
| 405 | <g transform="translate(40, 50)"> |
| 406 | <rect x="0" y="0" width="40" height="60" fill="#f7b955" rx="2"/> |
| 407 | <rect x="48" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/> |
| 408 | <rect x="96" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/> |
| 409 | <rect x="144" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/> |
| 410 | <rect x="192" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/> |
| 411 | <rect x="240" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/> |
| 412 | <rect x="288" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/> |
| 413 | <rect x="336" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/> |
| 414 | <text x="20" y="78" text-anchor="middle" fill="#f7b955" font-size="10">$0.001</text> |
| 415 | <text x="68" y="78" text-anchor="middle" fill="#8b949e" font-size="10">$0.002</text> |
| 416 | <text x="116" y="78" text-anchor="middle" fill="#8b949e" font-size="10">$0.005</text> |
| 417 | <text x="164" y="78" text-anchor="middle" fill="#8b949e" font-size="10">$0.01</text> |
| 418 | <text x="212" y="78" text-anchor="middle" fill="#8b949e" font-size="10">$0.05</text> |
| 419 | <text x="260" y="78" text-anchor="middle" fill="#8b949e" font-size="10">$0.2</text> |
| 420 | <text x="308" y="78" text-anchor="middle" fill="#8b949e" font-size="10">$0.5</text> |
| 421 | <text x="356" y="78" text-anchor="middle" fill="#8b949e" font-size="10">$1.0</text> |
| 422 | <text x="450" y="38" fill="#bc8cff" font-size="11">每个紫格里都有大量代币 X,标价远低于市价</text> |
| 423 | </g> |
| 424 | |
| 425 | <!-- Frame 2 --> |
| 426 | <text x="20" y="148" fill="#f7b955" font-size="13" font-weight="700">② 机器人灌入 USDC,吞掉第一格 bin(按 $0.001 买)</text> |
| 427 | <g transform="translate(40, 164)"> |
| 428 | <rect x="0" y="0" width="40" height="60" fill="#3fb950" opacity="0.5" stroke="#3fb950" stroke-width="2" rx="2"/> |
| 429 | <rect x="48" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/> |
| 430 | <rect x="96" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/> |
| 431 | <rect x="144" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/> |
| 432 | <rect x="192" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/> |
| 433 | <rect x="240" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/> |
| 434 | <rect x="288" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/> |
| 435 | <rect x="336" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/> |
| 436 | <text x="20" y="78" text-anchor="middle" fill="#3fb950" font-size="10" font-weight="700">已吃完</text> |
| 437 | <text x="450" y="38" fill="#3fb950" font-size="11">代币 X 进入机器人钱包,bin #1 变成 USDC</text> |
| 438 | </g> |
| 439 | |
| 440 | <!-- Frame 3 --> |
| 441 | <text x="20" y="262" fill="#f7b955" font-size="13" font-weight="700">③ active bin 推到下一格,继续吃(按 $0.002, $0.005, $0.01... 一路买上去)</text> |
| 442 | <g transform="translate(40, 278)"> |
| 443 | <rect x="0" y="0" width="40" height="60" fill="#3fb950" opacity="0.5" stroke="#3fb950" stroke-width="1" rx="2"/> |
| 444 | <rect x="48" y="0" width="40" height="60" fill="#3fb950" opacity="0.5" stroke="#3fb950" stroke-width="1" rx="2"/> |
| 445 | <rect x="96" y="0" width="40" height="60" fill="#3fb950" opacity="0.5" stroke="#3fb950" stroke-width="1" rx="2"/> |
| 446 | <rect x="144" y="0" width="40" height="60" fill="#3fb950" opacity="0.5" stroke="#3fb950" stroke-width="1" rx="2"/> |
| 447 | <rect x="192" y="0" width="40" height="60" fill="#3fb950" opacity="0.5" stroke="#3fb950" stroke-width="1" rx="2"/> |
| 448 | <rect x="240" y="0" width="40" height="60" fill="#f7b955" rx="2"/> |
| 449 | <rect x="288" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/> |
| 450 | <rect x="336" y="0" width="40" height="60" fill="#bc8cff" opacity="0.85" rx="2"/> |
| 451 | <text x="450" y="38" fill="#f7b955" font-size="11">active bin 一格格往上推,直到逼近真实市价 $1.0</text> |
| 452 | </g> |
| 453 | |
| 454 | <!-- Frame 4 --> |
| 455 | <text x="20" y="378" fill="#f7b955" font-size="13" font-weight="700">④ 同一笔 transaction 里把吃到的 X 全部发到 Raydium 按 $1.0 卖出</text> |
| 456 | <g transform="translate(40, 394)"> |
| 457 | <rect x="0" y="0" width="80" height="60" fill="#161b22" stroke="#bc8cff" stroke-width="1.5" rx="6"/> |
| 458 | <text x="40" y="26" text-anchor="middle" fill="#bc8cff" font-size="11" font-weight="700">Meteora</text> |
| 459 | <text x="40" y="44" text-anchor="middle" fill="#8b949e" font-size="10">买 X · 极便宜</text> |
| 460 | |
| 461 | <text x="160" y="34" fill="#f7b955" font-size="20">→</text> |
| 462 | |
| 463 | <rect x="200" y="0" width="80" height="60" fill="#161b22" stroke="#3fb950" stroke-width="1.5" rx="6"/> |
| 464 | <text x="240" y="26" text-anchor="middle" fill="#3fb950" font-size="11" font-weight="700">Raydium</text> |
| 465 | <text x="240" y="44" text-anchor="middle" fill="#8b949e" font-size="10">卖 X · 市价</text> |
| 466 | |
| 467 | <text x="320" y="34" fill="#f7b955" font-size="20">→</text> |
| 468 | |
| 469 | <rect x="360" y="0" width="120" height="60" fill="#161b22" stroke="#f7b955" stroke-width="1.5" rx="6"/> |
| 470 | <text x="420" y="26" text-anchor="middle" fill="#f7b955" font-size="11" font-weight="700">机器人钱包</text> |
| 471 | <text x="420" y="44" text-anchor="middle" fill="#f7b955" font-size="10" font-weight="700">+ $400K USDC</text> |
| 472 | |
| 473 | <text x="540" y="38" fill="#8b949e" font-size="11">所有指令打包进 1 笔 tx,</text> |
| 474 | <text x="540" y="54" fill="#8b949e" font-size="11">失败回滚 → 永远不会亏</text> |
| 475 | </g> |
| 476 | </svg> |
| 477 | <figcaption>套利的整个动作:低价吃光 → 市价卖出 → 同一笔交易原子完成。</figcaption> |
| 478 | </figure> |
| 479 | |
| 480 | <h3>事件时间线</h3> |
| 481 | <table> |
| 482 | <thead> |
| 483 | <tr><th>时刻</th><th>角色</th><th>动作</th><th>结果</th></tr> |
| 484 | </thead> |
| 485 | <tbody> |
| 486 | <tr><td>T+0s</td><td>鲸鱼</td><td>大额 swap,第一次"打开"这个被遗忘的池子</td><td>active bin 开始移动</td></tr> |
| 487 | <tr><td>T+0.4s</td><td>Bot 1</td><td>监听器触发,发起反向套利</td><td>$0.16 成本 → $403K 盈利</td></tr> |
| 488 | <tr><td>T+1.2s</td><td>Bot 2</td><td>下一个 block 抢落地</td><td>$0.23 成本 → $696K 盈利</td></tr> |
| 489 | <tr><td>T+0~150s</td><td>Bot 3-6</td><td>剩余 bin 慢慢被扫</td><td>累计接近 $3M</td></tr> |
| 490 | <tr><td>T+150s</td><td>—</td><td>active bin 已逼近 Raydium 市价</td><td>套利窗口关闭</td></tr> |
| 491 | </tbody> |
| 492 | </table> |
| 493 | |
| 494 | <h2>5. 技术实现骨架(如果你想做)</h2> |
| 495 | |
| 496 | <ol class="steps"> |
| 497 | <li><strong>扫描全网 Meteora DLMM 池找"stale"标的。</strong>拉所有 DLMM 池的 PDA 账户,用 SDK 读 active bin 价格,跟 Jupiter / Birdeye 拿到的市价对比。差 5-10% 以上的进候选池。这一步可以离线一天扫一次。</li> |
| 498 | <li><strong>实时盯盘候选池。</strong>用 Helius / Triton 的 Geyser gRPC 或 <code>accountSubscribe</code> WebSocket 订阅这些池子账户,状态一变就 push 给你(延迟 50-200ms)。</li> |
| 499 | <li><strong>盈亏模拟。</strong>调用 <code>@meteora-ag/dlmm</code> 模拟从这个池子买 X、再到 Raydium 卖的总收益。扣掉两边手续费、Solana 优先费、Jito tip 后还赚才发交易。</li> |
| 500 | <li><strong>原子 transaction。</strong>把"Meteora 买 + Raydium 卖"两条 swap 指令打到同一笔 tx 里,每个指令都设 <code>min_out_amount</code>,价格滑了整笔回滚。账户多塞不下时用 Address Lookup Table (LUT) 压缩。</li> |
| 501 | <li><strong>抢落地。</strong>普通 RPC 抢不过别人。主流做法:Jito bundle(带 tip)+ Helius Sender / Triton Cascade 同时发出去,哪个先 land 算哪个。tip 大小是 bot 之间最常调的旋钮。</li> |
| 502 | </ol> |
| 503 | |
| 504 | <div class="callout"> |
| 505 | <strong>提示:</strong>这种机会真正的瓶颈不在策略本身,而在<strong>抢速度</strong>和<strong>风险控制</strong>。一旦池子被发现,每个 block 都有 5-20 个机器人在抢同一笔利润。原子 + Jito + 充足的 priority fee 是最低门槛。 |
| 506 | </div> |
| 507 | |
| 508 | <h2>6. 一句话总结</h2> |
| 509 | <div class="quote"> |
| 510 | <strong>Bin 不会自动更新价格</strong>,DLMM 池子只在被交易时才移动 active bin。LP 创建池子后 10 个月不维护,外部市场暴涨 1000 倍——池子里那些"高于原始 active bin 但远低于市场价"的 bins 就成了静悄悄挂着的「便宜代币堆」,等着第一个发现它的套利机器人一口气全吃光。 |
| 511 | </div> |
| 512 | |
| 513 | <p>这就是 stale pool / stale LP arbitrage。错不在 Meteora 协议,而在 LP 自己。这种漏洞跟 Uniswap v3 在以太坊上遇到的"untracked LP"问题完全一样,只是 Solana 上池子多、LP 散、被发现的速度极快——这次连 1 秒都没撑住。</p> |
| 514 | |
| 515 | <footer> |
| 516 | <p>资料来源: |
| 517 | <a href="https://x.com/anshu_code/status/2051888998671581324" target="_blank">@anshu_code 主推</a> · |
| 518 | <a href="https://x.com/anshu_code/status/2051392982327468463" target="_blank">前一条引用</a> · |
| 519 | Meteora DLMM 文档 · Jupiter / Helius 公开技术资料 |
| 520 | </p> |
| 521 | <p>本文为根据公开链上数据和推文内容做的技术拆解,不构成投资建议;过期池套利涉及极高的速度竞争和资金风险,复刻前请自行评估。</p> |
| 522 | </footer> |
| 523 | |
| 524 | </div> |
| 525 | </body> |
| 526 | </html> |
| 527 |