Перейти к основному содержанию

Documentation Index

Fetch the complete documentation index at: https://docs.raydium.io/llms.txt

Use this file to discover all available pages before exploring further.

Эта страница переведена с помощью ИИ. За эталон принимается английская версия.Открыть английскую версию →
MEV на Solana отличается от MEV в Ethereum, основанного на мемпуле. Лидеры блоков видят пакеты транзакций по мере их поступления, а не как упорядоченный мемпул. Front-running происходит через переупорядочение на стороне лидера или через co-located поисковиков, а sandwich-атаки выполняются ботами, которые следят за состоянием пула и посылают вашу транзакцию с более высокой комиссией. Способы защиты соответственно различаются.

Введение в split routing

«Split routing» означает разделение одного логического своппа между несколькими пулами так, чтобы маржинальные цены выровнялись — такой же выход, как если бы каждый кусок менялся по цене своего пула. Это снижает эффективный ценовой импакт, когда какой-либо отдельный пул мелкий относительно размера торговли. Формулировка задачи: дано пулов P_1, ..., P_n с функциями f_i(x), которые преобразуют вход x в выход, найти разделение x_1 + ... + x_n = X, которое максимизирует Σ f_i(x_i). Поскольку каждое f_i вогнутое, оптимум удовлетворяет f'_1(x_1) = f'_2(x_2) = ... = f'_n(x_n) (равные маржинальные цены).

Жадная реализация

Простой подход, который на практике дает результат в пределах ~1% от оптимального:
remaining = X
routes    = []
step      = X / 1000     // размер среза
while remaining > 0:
    best_pool = argmax over i of f'_i(current_x_i + step)
    x_i += step
    routes.append((best_pool, step))
    remaining -= step
Меньший step → ближе к оптимальному, больше итераций. На практике 100–500 срезов — разумная золотая середина.

Реализация через выпуклую оптимизацию

Для production-grade агрегаторов решайте задачу оптимизации напрямую. Каждый пул имеет закрытую форму f'_i(x):
  • Constant-product (CPMM / AMM v4): f'(x) = y * R_y / (R_x + x)^2, где R_x, R_y — резервы и y = R_x * R_y / (R_x + x) - R_y … (более простая производная: маржинальная цена — это R_y / (R_x + x), поэтому разделение для выравнивания маржинальных цен — это одномерный поиск).
  • CLMM: кусочно-гладкая — внутри одного тика f'(x) — это рациональная функция sqrt_price; между тиками она скачет дискретно. Разделяйте с решателем малого шага или рассматривайте каждый смежный тик как собственный «пул».
Выход split routing — это вектор [(pool_1, x_1), (pool_2, x_2), ...], который этап сборки транзакции преобразует в последовательность инструкций swopp.

Когда split routing помогает

Размер торговли относительно TVLSplit помогает?
<0.1%Нет — один пул доминирует
0.1–1%Незначительно
1–5%Да, улучшение на 10–50 bps
>5%Да, значительное улучшение
Если вы запускаете in-UI swap кошелька для розничного пользователя, торгующего <$10k на глубоком пуле, не тратьте время на split — накладные расходы gas превышают улучшение. Для агрегатора, котирующего институциональный поток, всегда разделяйте.

Multi-hop маршруты

Когда прямого пула не существует или импакт прямого пула огромен, переходите через промежуточный токен:
tokenA → tokenHub → tokenB
Распространённые хабы: USDC, SOL, RAY. Каждый hop имеет:
  • Свой слиппедж-лимит (ниже на прямых hop-ах; per-hop на multi-hop).
  • Свою комиссию.
  • Свой ценовой импакт.
Общий импакт компонуется: (1 - impact_1) * (1 - impact_2). Импакт 1% twice — это 1.99% всего, не 2%. Никогда не переходите через один и тот же пул дважды. Прохождение A → B → A → B через один и тот же CLMM просто сжигает комиссии и слиппедж. Агрегаторы должны фильтровать такие маршруты при генерации. (Примечание: это зацикливание одной и той же пары, а не multi-hop в целом — маршрутизация A → USDC → B через разные пулы — это стандартный, полезный паттерн, одобренный выше.) Per-hop vs end-to-end минимум. С CPI composition (integration-guides/cpi-integration), вы можете установить для каждого hop minimum_amount_out на 0 и обеспечить один end-to-end минимум в вашем proxy. Без CPI каждый hop обеспечивает свой минимум, что требует вычисления разумных промежуточных границ — обычно quote_i * (1 - slippage_bps/10000) per hop.

Sandwich-атаки

Механизм

Бот следит за потоком транзакций в сети. Когда он видит ваш своп:
  1. Front-run: бот покупает тот же токен перед вами, поднимая цену пула.
  2. Ваша транзакция: вы меняете по худшей цене.
  3. Back-run: бот продаёт по повышенной цене, захватывая спред.
Бот платит priority fees за обе свои транзакции; прибыль — это разница sandwich минус двойной priority fee. Прибыльно только на пулах, где ваша торговля значимо движет цену.

Защита

Плотный слиппедж. Если ваш minimum-out на 0.5% ниже котировки, sandwich, который движет цену более чем на 0.5%, revert вашу транзакцию, но pre-trade бота уже прошёл по вашей старой цене. Они теряют деньги. Sandwich-боты ориентируются на широкий слиппедж (≥1–2%); слиппедж <0.3% в основном защищён. Private-mempool submission (Jito). Отправьте транзакцию как часть Jito bundle. Bundle не появляются в публичном потоке gossip; боты не могут видеть торговлю в полёте и front-run её. Компромисс: bundle требуют tip на стороне валидатора, и не все лидеры включают Jito (хотя большинство включают). Меньшие размеры торговли. Разделите торговлю на несколько транзакций, чтобы ни одна не двигала цену достаточно, чтобы быть выгодной целью sandwich. Увеличивает общие расходы gas. Randomization по времени. Отправляйте во время низких объёмов, если возможно. Недоступно для интерактивных user swap, но жизнеспособно для scheduled bot flow. CLMM пулы Raydium обычно видят меньше sandwich-активности, чем CPMM, потому что структура single-tick ликвидности означает, что небольшие торговли не двигают цену вообще (они остаются внутри тика). Глубокие CLMM пулы — органически лучшее место для sandwich-защиты.

Jito bundles

Jito — это модифицированный Solana валидатор-клиент, который принимает bundles — упорядоченные группы транзакций, приземляющихся атомарно. Боты используют Jito для извлечения MEV; обычные пользователи используют Jito для защиты от этих же ботов.

Как работают bundles

  • Подключитесь к Jito block engine endpoint (например https://mainnet.block-engine.jito.wtf).
  • Отправьте bundle из 1–5 транзакций плюс tip на один из Jito tip аккаунтов.
  • Если текущий лидер запускает Jito, ваш bundle рассматривается. Bundle-победитель по аукциону этого слота (bundle с самым высоким tip-per-CU) приземляется; остальные отваливаются.

Размер tip

Размеры tip следуют распределению recent-bundle. Jito публикует real-time процентили:
const tipRes = await fetch("https://worker.jito.wtf/api/v1/bundles/tip_floor");
const tips   = await tipRes.json();
// { ema_landed_tips_25th_percentile, 50th, 75th, 95th, 99th }

// User-facing swap в обычный день — 50th процентиль достаточно.
const tipSol = tips.ema_landed_tips_50th_percentile_lamports / 1e9;

// Time-sensitive bot trade при congestion — 75–95th процентиль.
Типичные диапазоны: 0.0001–0.001 SOL для non-urgent user swap; 0.01–0.1 SOL при congestion для high-priority ботов.

Конструирование bundle

import { SearcherClient } from "jito-ts";

const client = new SearcherClient("https://mainnet.block-engine.jito.wtf");

const tipIx = SystemProgram.transfer({
  fromPubkey: user.publicKey,
  toPubkey:   JITO_TIP_ACCOUNTS[Math.floor(Math.random() * 8)],  // 8 tip аккаунтов
  lamports:   tipLamports,
});

const tx1 = new VersionedTransaction(...);  // своп
tx1.sign([user]);

const bundleUuid = await client.sendBundle([tx1], tipLamports);
// Опционально: await confirmation via client.getBundleStatuses([bundleUuid])
Подводные камни:
  • Tip должен быть в bundle. Включите SystemProgram.transfer на Jito tip аккаунт как инструкцию внутри одной из транзакций bundle (обычно последнюю). Отдельная tip транзакция, не являющаяся частью bundle, игнорируется.
  • Лидер не включает Jito. ~75% лидеров запускают Jito; ~25% нет. Bundle, отправленные, когда non-Jito лидер держит слот, отваливаются. Клиент автоматически повторит попытку.
  • Expiry. Bundle используют ту же модель blockhash-expiry, что и обычные транзакции. Собирайте и отправляйте быстро; ~60s окно.

Bundle vs priority fees

Priority fees подкупают лидера, чтобы он включил вашу транзакцию раньше. Jito bundle дополнительно скрывают транзакцию из публичного мемпула. Используйте priority fees для срочности; используйте bundle для sandwich-защиты. Оба средства вместе: используйте оба на high-value user swap. Смотрите integration-guides/priority-fee-tuning для sizing priority fees.

MEV-share / revert-protected RPC

Некоторые RPC провайдеры предлагают «MEV-share» или «revert-protected» endpoints, которые внутренне маршрутизируют вашу транзакцию через Jito bundle или эквивалентные приватные пути:
  • Helius — staked соединения с bundle поддержкой.
  • QuickNode — «Revert Protect» endpoint; автоматически формирует bundle вокруг отправленных транзакций.
  • Triton — private-flow tier.
Использование одного из этих — самый простой путь для проектов, которые не хотят управлять logic bundle сами. Компромисс: непрозрачные internals; вы доверяете construction bundle провайдера.

Обработка congestion

Во время windows высокого объёма (mainnet launch, major listing, sustained rally), очереди пакетов лидера переполняются. Признаки:
  • Транзакции остаются unconfirmed 60+ секунд, затем expires с “blockhash not found”.
  • Priority fees, которые работали вчера, недостаточны сегодня.
  • Simulation успешна, но execution никогда не приземляется.
Стратегии:
  1. Агрессивный retry при expiry. На TransactionExpiredBlockheightExceeded, переб постройте с fresh blockhash и переотправьте. Не retry на revert — reverts детерминированы.
  2. Multi-RPC broadcast. Отправьте одну и ту же транзакцию на несколько RPC параллельно; какая бы достигла лидера первой, побеждает.
  3. Priority fee ramping. Начните с 50th процентиля; если первая попытка expires, retry на 75th, затем 95th.
  4. Jito bundle как fallback. Jito лидеры обычно менее congested, потому что block engine сортирует bundle по tip-per-CU; high-tip bundle получают приоритет.
  5. Simulate less. При congestion симулируйте один раз в начале; не re-simulate при retry, так как pool state всё равно сдвинется. Re-simulation при congestion часто падает spuriously.

Per-product MEV соображения

CPMM. Высоко sandwichable на low-TVL пулах. Constant-product кривая амплифицирует даже небольшие bot pre-trade. Рекомендуем Jito bundle для любого CPMM trade >0.5% pool TVL. CLMM. Менее sandwichable на глубоких пулах, потому что within-tick торговля не двигает цену. Но cross-tick торговля абсолютно двигает; sandwich на tick crossing — известный паттерн. Плотный слиппедж (<0.3%) — лучшая защита. AMM v4 + OpenBook. OpenBook orderbook fills работают через одну транзакцию, поэтому sandwich-боты, которые не знают orderbook state, недооценивают ценовой импакт и часто fail. Органически low-MEV venue по этой причине. LaunchLab. Во время early-bonding-curve фазы front-running распространён на hyped launch. Кривые двигаются быстро и слиппедж широк. Jito bundle сильно рекомендуются. После graduation результирующий CPMM следует обычной CPMM динамике. Farms. Harvest и stake операции — не своп и не sandwichable. Нет специальной обработки.

Контрольный список

Для production агрегатора / wallet swap UI:
  • Слиппедж по умолчанию ≤0.5% на обычных парах; пользователь может переопределить.
  • Jito bundle submission включён по умолчанию для swap >$1k USD значение.
  • Priority fee взят из live estimate (не hard-coded).
  • Retry логика различает revert (не retry) от expiry (retry с новым blockhash).
  • Multi-hop маршруты устанавливают per-hop минимумы, не end-to-end.
  • Split routing активен для торговли >1% TVL любого отдельного пула.
  • Pool freshness: re-fetch состояние прямо перед submission; re-quote если stale.
  • Sandwich-resistant на shallow пулах: либо Jito-only, либо reject если слиппедж >1%.

Указатели

Источники: