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

Обзор

Metis — это низкоуровневый движок маршрутизации свопов Jupiter. Он предоставляет детальный контроль над каждым аспектом своп-транзакции: выбор маршрута, компоновка инструкций, комиссии за приоритет, лимиты вычислительных единиц, slippage и трансляция. В отличие от Jupiter Ultra, Metis требует управлять этим самостоятельно — но взамен даёт максимальную гибкость для интеграций, которым нужен CPI, пользовательские инструкции или специфическая стратегия трансляции транзакций. Используйте Metis, когда вам нужно:
  • CPI (Cross Program Invocation) с вашей собственной программой на блокчейне
  • Компоновка пользовательских инструкций вокруг свопа
  • Полный контроль над комиссиями за приоритет, вычислительными единицами и slippage
  • Собственный RPC или трансляция через Jito
Рассмотрите Ultra вместо Metis, если:
  • Вам нужно сквозное исполнение без управления slippage, комиссиями или трансляцией
  • Вам нужен Real-Time Slippage Estimator (RTSE), доступный только через Ultra
Доступ к Metis Swap API через OrbitFlare доступен на Dedicated Nodes через путь /jup на эндпоинте вашего узла. Для размещённого Jupiter API используйте https://api.jup.ag/swap/v1/... с вашим x-api-key.

Как это работает

1

Получить котировку

Вызовите GET /swap/v1/quote с входным минтом, выходным минтом, суммой и slippage, чтобы получить лучший маршрут от движка маршрутизации Metis.
2

Создать своп-транзакцию

Отправьте ответ с котировкой на POST /swap/v1/swap. API возвращает сериализованную транзакцию, готовую к подписанию, с опциональной оценкой комиссии за приоритет, динамическими лимитами вычислительных единиц и динамическим slippage.
3

Подписать и отправить

Десериализуйте base64-транзакцию, подпишите её вашим кошельком и транслируйте через connection.sendRawTransaction() или через Jito.

Шаг 1 — Получить котировку

Эндпоинт: GET /swap/v1/quote

Обязательные параметры

ПараметрОписание
inputMintАдрес минта входного токена (например, So11111111111111111111111111111111111111112 для SOL)
outputMintАдрес минта выходного токена (например, EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v для USDC)
amountЦелочисленная сумма входных токенов до десятичного знака (lamports для SOL, атомарные единицы для остальных)
slippageBpsМаксимально допустимый slippage в basis points (например, 50 = 0,5%)

Пример — Котировка 1 SOL в USDC

const quoteResponse = await (
  await fetch(
    "https://api.jup.ag/swap/v1/quote?" +
      "inputMint=So11111111111111111111111111111111111111112" +
      "&outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" +
      "&amount=100000000" +
      "&slippageBps=50" +
      "&restrictIntermediateTokens=true",
    {
      headers: {
        "x-api-key": "YOUR_API_KEY",
      },
    }
  )
).json();

console.log(JSON.stringify(quoteResponse, null, 2));

Пример ответа

{
  "inputMint": "So11111111111111111111111111111111111111112",
  "inAmount": "100000000",
  "outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  "outAmount": "16198753",
  "otherAmountThreshold": "16117760",
  "swapMode": "ExactIn",
  "slippageBps": 50,
  "platformFee": null,
  "priceImpactPct": "0",
  "routePlan": [
    {
      "swapInfo": {
        "ammKey": "5BKxfWMbmYBAEWvyPZS9esPducUba9GqyMjtLCfbaqyF",
        "label": "Meteora DLMM",
        "inputMint": "So11111111111111111111111111111111111111112",
        "outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
        "inAmount": "100000000",
        "outAmount": "16198753"
      },
      "percent": 100
    }
  ],
  "contextSlot": 299283763,
  "timeTaken": 0.015257836
}
outAmount — лучший возможный результат на момент котировки. slippageBps не влияет на это значение — он управляет otherAmountThreshold, то есть минимумом, который вы готовы принять.

Дополнительные параметры котировки

Установите значение true, чтобы маршрутизация проходила только через высоколиквидные промежуточные токены. Это снижает сбои маршрутизации из-за неликвидных путей и повышает стабильность маршрута с незначительной потенциальной потерей в цене.
Установите значение true, чтобы ограничить маршрутизацию одним рынком (без мультихопа). Возвращает пустую котировку, если прямого маршрута нет. Может давать невыгодные цены — используйте с осторожностью.
Ограничивает общее количество аккаунтов во внутренних инструкциях свопа. Полезно, когда нужно оставить место для собственных инструкций. Рекомендуемый минимум — 64. Слишком низкое значение исключает DEX, требующие большего числа аккаунтов (например, Meteora DLMM требует до 47).
Добавьте комиссию к свопу, которая зачисляется на ваш feeAccount. Комбинируйте с feeAccount в запросе свопа.

Шаг 2 — Создать своп-транзакцию

Эндпоинт: POST /swap/v1/swap Передайте ответ с котировкой из шага 1 вместе с публичным ключом пользователя. API возвращает сериализованную base64-транзакцию, готовую к подписанию и отправке.

Создание транзакции с оптимизацией подтверждения

const swapResponse = await (
  await fetch("https://api.jup.ag/swap/v1/swap", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": "YOUR_API_KEY",
    },
    body: JSON.stringify({
      quoteResponse,
      userPublicKey: wallet.publicKey.toString(),

      // Optimize for transaction landing
      dynamicComputeUnitLimit: true,
      dynamicSlippage: true,
      prioritizationFeeLamports: {
        priorityLevelWithMaxLamports: {
          maxLamports: 1000000,
          priorityLevel: "veryHigh",
        },
      },
    }),
  })
).json();

Пример ответа

{
  "swapTransaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAGDkS+...",
  "lastValidBlockHeight": 279632475,
  "prioritizationFeeLamports": 9999,
  "computeUnitLimit": 388876,
  "prioritizationType": {
    "computeBudget": {
      "microLamports": 25715,
      "estimatedMicroLamports": 785154
    }
  },
  "dynamicSlippageReport": {
    "slippageBps": 50,
    "otherAmount": 20612318,
    "simulatedIncurredSlippageBps": -18,
    "amplificationRatio": "1.5",
    "categoryName": "lst",
    "heuristicMaxSlippageBps": 100
  },
  "simulationError": null
}

Уровни комиссий за приоритет

priorityLevelПерцентильСлучай использования
medium25-йНизкая нагрузка, не чувствительные ко времени операции
high50-йСтандартные торговые условия
veryHigh75-йКонкурентные условия, MEV, чувствительные ко времени свопы
Установите maxLamports как предохранительный ограничитель для предотвращения переплаты во время скачков комиссий:
prioritizationFeeLamports: {
  priorityLevelWithMaxLamports: {
    maxLamports: 10000000,
    global: false,        // use local fee market (writable accounts), not global
    priorityLevel: "veryHigh",
  },
}

Использование инструкций свопа вместо транзакции

Если вам нужно скомпоновать своп с вашими собственными инструкциями, используйте /swap/v1/swap-instructions вместо /swap/v1/swap. Принимает те же параметры, но возвращает отдельные объекты инструкций, а не сериализованную транзакцию:
const instructions = await (
  await fetch("https://api.jup.ag/swap/v1/swap-instructions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": "YOUR_API_KEY",
    },
    body: JSON.stringify({
      quoteResponse,
      userPublicKey: wallet.publicKey.toString(),
    }),
  })
).json();

const {
  computeBudgetInstructions,
  setupInstructions,
  swapInstruction: swapInstructionPayload,
  cleanupInstruction,
  addressLookupTableAddresses,
} = instructions;

Шаг 3 — Отправить своп-транзакцию

Десериализация, подпись и сериализация

Поле swapTransaction в ответе закодировано в base64. Десериализуйте его в VersionedTransaction, подпишите, затем конвертируйте обратно в бинарный формат для отправки:
import { VersionedTransaction } from "@solana/web3.js";

const transactionBase64 = swapResponse.swapTransaction;
const transaction = VersionedTransaction.deserialize(
  Buffer.from(transactionBase64, "base64")
);

transaction.sign([wallet]);

const transactionBinary = transaction.serialize();

Отправка транзакции

const signature = await connection.sendRawTransaction(transactionBinary, {
  maxRetries: 2,
  skipPreflight: true,
});
ПараметрОписание
maxRetriesМаксимальное количество повторных попыток перед отказом. Если не указан, RPC повторяет попытки до истечения срока действия blockhash.
skipPreflightПропустить проверку подписи и симуляцию перед отправкой. Рекомендуется true для чувствительных ко времени свопов.

Подтверждение транзакции

const confirmation = await connection.confirmTransaction(
  { signature },
  "finalized"
);

if (confirmation.value.err) {
  throw new Error(
    `Transaction failed: ${JSON.stringify(confirmation.value.err)}\nhttps://solscan.io/tx/${signature}/`
  );
} else {
  console.log(`Transaction successful: https://solscan.io/tx/${signature}/`);
}

Трансляция через Jito

Чтобы использовать Jito для более быстрого включения и защиты от MEV, замените prioritizationFeeLamports чаевыми Jito и отправьте на эндпоинт Jito RPC:
body: JSON.stringify({
  quoteResponse,
  userPublicKey: wallet.publicKey.toString(),
  prioritizationFeeLamports: {
    jitoTipLamports: 1000000, // fixed lamports, not a cap
  },
}),
Чаевые Jito должны быть отправлены на эндпоинт Jito RPC, чтобы вступить в силу. Отправка на стандартный RPC не будет маршрутизирована через блок-движок Jito.

Расширенные возможности

Включите dynamicSlippage: true в запросе свопа. Бэкенд симулирует транзакцию и применяет эвристику на основе категории токена для оценки подходящего значения slippage.Примечание: Разработка Dynamic Slippage прекращена. Для использования в продакшене Jupiter рекомендует Ultra Swap API, включающий Real-Time Slippage Estimator (RTSE) — значительно более точную и реактивную систему.
Начиная с января 2025 года, Jupiter Swap через CPI является рекомендуемым методом интеграции с программами на блокчейне. Добавьте крейт jupiter-cpi в вашу программу:
[dependencies]
jupiter-cpi = { git = "https://github.com/jup-ag/jupiter-cpi", rev = "5eb8977" }
Затем вызовите маршрут общих аккаунтов из обработчика инструкции. Полный пример реализации смотрите в примере Jupiter CPI.
Flash Fill был более ранним обходным решением для ограничений размера аккаунтов CPI с использованием Versioned Transactions и Address Lookup Tables. Больше не рекомендуется, поскольку ослабление ограничений CPI активно на мейннете Solana. Смотрите sol-swap-flash-fill для исторической справки.
При использовании maxAccounts, вот приблизительное количество аккаунтов для каждого DEX (минимум применяется при наличии ALT, максимум — без них):
DEXМакс. аккаунтовМин. аккаунтов
Meteora DLMM4719
Meteora4518
Raydium4518
Raydium CLMM4519
Moonshot3715
Raydium CPMM3714
Pumpfun AMM4217
Pumpfun Bonding Curve4016
Orca Whirlpool3012
Obric3012
Solfi229
Sanctum8080
Sanctum Infinity8080
Слишком низкое значение maxAccounts молча исключит DEX из маршрутизации. Держите его как можно выше и снижайте только если транзакция превышает ограничение в 1232 байта.

Требования и ресурсы

Доступ к Metis Swap API через OrbitFlare доступен только на Dedicated Nodes. API доступен через путь /jup на эндпоинте вашего выделенного узла или напрямую по адресу https://api.jup.ag/swap/v1/... с ключом Jupiter API.