Для резолверів

Виконання обмінів проти ліквідності Aqua: що потрібно для ролі резолвера, як відбувається ончейн-виконання і як отримати котирування та виконати обмін через SwapVM SDK.

10 хв читанняОновлено липень 2026 р.

Що роблять резолвери в Aqua

По всій 1inch Network обміни, що виконують ліквідність Aqua, маршрутизуються 1inch Resolvers: маркетмейкерами та арбітражними трейдерами, які проходять процес верифікації 1inch.

Сама Aqua не займається ні пошуком, ні метчингом. За один виклик обміну вона оцінює та виконує одну позицію проти одного контрагента. Яку позицію і в якому обсязі виконувати, вирішує резолвер: маршрутизація 1inch передає обміни мережі, а ваші власні системи можуть викликати контракти напряму.

Вперше знайомитеся з базовою механікою? Почніть із розділу як працює Aqua.

Перш ніж почати

  • Верифікація резолвера через портал 1inch Business, яка видає токен доступу для кожної мережі
  • Токен доступу на операторській EOA, що надсилає транзакцію обміну
  • Доступ до ноди в цільовій мережі, а також вхідні токени та газ для виконань
  • Канонічні адреси контрактів, однакові в кожній підтримуваній мережі й наведені нижче
  • API-ключ із Business portal для шляху пошуку через Aqua API

Адреси контрактів

Aqua та роутер SwapVM розгорнуті детерміновано, з однаковою адресою в кожній підтримуваній мережі. Взаємодійте лише з цими двома контрактами. Усе інше не є Aqua.

Aqua (реєстр)
0x1111113ccf1426a8e30e2bff5e005d929bf6a90a
Роутер SwapVM
0x111111338c5091e8440b67b168bae16a668ac0de

Як відбувається виконання

  1. 1

    Пройдіть верифікацію

    Завершіть онбординг резолвера через портал Business і отримайте токен доступу для кожної мережі, де ви виконуєте обміни.

  2. 2

    Знайдіть позицію

    Отримуйте відкриті позиції з Aqua API, відновлюйте живий набір за подіями Shipped і Docked або приймайте свопи з маршрутизації 1inch як мережевий резолвер.

  3. 3

    Котируйте безпосередньо перед надсиланням

    Викличте функцію quote роутера статичним викликом з декодованим ордером. Котирування читають живе забезпечення гаманця і змінюються, щойно надходять інші виконання.

  4. 4

    Задайте межі

    Виведіть мінімальний вихід зі свіжого котирування та встановіть короткий дедлайн. Тоді зміщена крива відкотить транзакцію замість виконання за гіршим курсом.

  5. 5

    Виконайте обмін і перевірте

    Надішліть обмін з EOA, що володіє токеном доступу, вимагайте успішний чек і читайте виконані суми з події Swapped.

Швидкий старт: котирування та виконання однієї позиції

Приклади нижче знаходять відкриту позицію через Aqua API, беруть котирування та надсилають захищене виконання. Вони працюють на Node 22 з TypeScript SDK. Контракти, події, SDK та API називають позицію strategy, тому код також.

Встановіть SDKbash
pnpm add @1inch/aqua-sdk @1inch/swap-vm-sdk viem
Пошук відкритих позицій через Aqua APItypescript
import assert from 'node:assert'
import { HexString, Order } from '@1inch/swap-vm-sdk'

// Every currently open position from every LP, newest first. Auth is the
// same API key the other 1inch APIs use - issued in the Business portal.
const response = await fetch('https://api.1inch.com/aqua/v1.0/strategies/opened?chainIds=1&limit=50', {
  headers: { Authorization: `Bearer ${process.env.ONEINCH_API_KEY}` },
})
assert(response.ok, `Aqua API responded ${response.status}`)
const { items, nextCursor } = await response.json()

// Pick by your own criteria. Per-token balance and allowance in each item are
// useful pre-filters, but they are indexed with a delay - only a fresh
// on-chain quote() is authoritative.
const strategy = items[0]
const order = Order.decode(new HexString(strategy.strategyBytes))
console.log('candidate:', strategy.strategyHash, 'tokens:', strategy.tokens.length, 'more:', nextCursor !== null)
Котирування, виконання та перевіркаtypescript
import assert from 'node:assert'
import { ABI, Address, HexString, Order, SwappedEvent, SwapVMContract, TakerTraits } from '@1inch/swap-vm-sdk'
import { createPublicClient, createWalletClient, decodeFunctionResult, erc20Abi, http, isHex } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { mainnet } from 'viem/chains'

// The SwapVM router is a deterministic deployment - the same address on every
// supported chain (see the contract addresses above). The strategy bytes come
// from the Aqua API or a Shipped event. The operator key belongs to the EOA
// holding the resolver credential - it must be the transaction origin.
const router = new Address('0x111111338c5091e8440b67b168bae16a668ac0de')
const strategyBytes = process.env.STRATEGY_BYTES
const operatorPrivateKey = process.env.OPERATOR_PRIVATE_KEY
assert(isHex(strategyBytes) && isHex(operatorPrivateKey))

const order = Order.decode(new HexString(strategyBytes))

const USDC = new Address('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48')
const WETH = new Address('0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2')
const amountIn = 3_000n * 10n ** 6n

const operator = privateKeyToAccount(operatorPrivateKey)
const publicClient = createPublicClient({ chain: mainnet, transport: http() })
const wallet = createWalletClient({ chain: mainnet, transport: http(), account: operator })

// 1. Preview the swap with a static quote() call, right before you submit.
const quoteTx = SwapVMContract.buildQuoteTx(router, {
  order,
  tokenIn: USDC,
  tokenOut: WETH,
  amount: amountIn,
  takerTraits: TakerTraits.new({ exactIn: true }),
})
const quoted = await publicClient.call({ to: quoteTx.to, data: quoteTx.data })
assert(quoted.data, 'quote call returned no data')
const [, quotedOut] = decodeFunctionResult({ abi: ABI.SWAP_VM_ABI, functionName: 'quote', data: quoted.data })
console.log('quoted output:', quotedOut)

// 2. Approve the router to move the input token (once per token and chain).
const approveHash = await wallet.writeContract({
  address: USDC.toString(),
  abi: erc20Abi,
  functionName: 'approve',
  args: [router.toString(), amountIn],
})
await publicClient.waitForTransactionReceipt({ hash: approveHash })

// 3. Swap with a minimum-output floor and a short deadline. Balances and
// curves move between quote and fill - never submit unprotected traits.
const swapTx = SwapVMContract.buildSwapTx(router, {
  order,
  tokenIn: USDC,
  tokenOut: WETH,
  amount: amountIn,
  takerTraits: TakerTraits.new({
    exactIn: true,
    threshold: (quotedOut * 995n) / 1_000n,
    deadline: BigInt(Math.floor(Date.now() / 1_000) + 60),
  }),
})
const fillHash = await wallet.sendTransaction(swapTx)
const receipt = await publicClient.waitForTransactionReceipt({ hash: fillHash })
assert(receipt.status === 'success', 'fill reverted')

// 4. Verify the executed amounts from the router's Swapped event.
const swappedLog = receipt.logs.find((log) => log.topics[0] === SwappedEvent.TOPIC.toString())
assert(swappedLog, 'no Swapped event in the receipt')
const swapped = SwappedEvent.fromLog(swappedLog)
console.log('filled:', swapped.amountIn, '->', swapped.amountOut)

Спершу прожeніть цикл на форку

Усе описане вище безпечно репетирується на локальному форку. Anvil від Foundry клонує мережу на певному блоці, тож ви отримуєте справжні контракти та справжні живі позиції, нічим не ризикуючи. З увімкненими безключовими надсиланнями можна діяти від імені будь-якої EOA, що вже тримає токен доступу, і пройти весь цикл, пошук, котирування, виконання, перевірку, до першої реальної транзакції.

Прогін на форку мейннетуbash
# Real Aqua bytecode and live positions at the forked block, zero real spend
anvil --fork-url $YOUR_RPC_URL --auto-impersonate

# Point the quickstart at http://127.0.0.1:8545 and send from any EOA that
# already holds the per-chain access token - no key needed on a fork

Як виконання проходить ончейн

Виконання — це одна транзакція до роутера SwapVM. Роутер виконує програму інструкцій позиції: спершу перевірку доступу, потім цінову криву, потім переміщення токенів. Вхідний токен іде з вашої EOA до гаманця LP, а вихідний повертається вам у тій самій транзакції, тим часом реєстр Aqua оновлює баланси позиції та емітує Pushed і Pulled. Ніщо в жоден момент не лежить у пулі.

Усе за принципом усе або нічого. Якщо під час виконання не проходить будь-яка перевірка, немає токена доступу, не виконано threshold, минув deadline або замало забезпечення гаманця, уся транзакція ревертиться і токени не рухаються. Ревертнуте виконання коштує газу і більше нічого.

Роутер надає quote і swap, і обидва проходять той самий ціновий шлях. Жоден не є view-функцією, тож викликайте quote через eth_call статично: він читає живий стан, оцінює саме той своп, який ви передали, і нічого не змінює. Цей перегляд не є зобов’язанням. Виконання зв’язують лише traits, надіслані разом зі swap.

TakerTraits несуть ваші межі виконання в транзакцію. Їх забезпечує мережа, а не SDK. Найуживаніші:

exactIn
Який бік фіксований. За точного входу ви задаєте суму надсилання і обмежуєте отримання. За точного виходу фіксуєте отримання і обмежуєте надсилання.
threshold
Сама межа. За точного входу це мінімальний вихід, який виконання зобов’язане дати. За точного виходу — стеля ваших витрат. Нульовий threshold вимикає перевірку, тож завжди виводьте його зі свіжого котирування.
deadline
Термін у unix-секундах. Транзакція, замайнена пізніше, ревертиться, тому виконання, що застрягло в мемпулі, не може пройти за застарілою кривою.
customReceiver
Доставляє вихід на адресу, відмінну від EOA-відправника, наприклад на казначейську. Перевірка доступу все одно виконується за джерелом транзакції.
shouldUnwrap
Отримуйте нативний токен замість обгорнутої форми, коли вихід — обгорнутий нативний токен.

Не довіряйте результату за власними розрахунками. Вимагайте успішний receipt, а тоді декодуйте подію Swapped роутера: orderHash, maker, taker, tokenIn, tokenOut, amountIn і amountOut. Це виконані суми, і саме їх має фіксувати ваш облік.

Сценарії збоїв, які треба обробляти

Закрита позиція
LP може закрити позицію будь-коли, і після цього читання балансу ревертяться. Позиція, якої немає в списку opened в API, закрита, але між опитуваннями джерелом істини лишається ончейн-реверт. Перевіряйте знову перед кожним виконанням.
Низьке забезпечення гаманця
Позиції котируються зі спільного балансу гаманця. Конкурентне виконання може спустошити його першим, і ваш переказ ревертнеться, хоча котирування виглядало добре. Поля balance та allowance з API допомагають у попередньому відборі, але індексуються із затримкою. Авторитетним є лише свіже ончейн-котирування.
Зміщена ціна
Котирування старіють у міру надходження виконань і плину часу. Мінімальний вихід і дедлайн перетворюють погане виконання на чистий відкат.
Немає токена доступу
Без облікових даних на джерелі транзакції позиції, створені в dApp, відкочують обмін ще до розрахунку ціни.
Неканонічні адреси
Aqua — це лише дві канонічні адреси вище. Виконання, надіслані будь-куди інде, не досягають живих позицій. Немає чого перевіряти по мережах, розгортання скрізь однакові.

Робота в продакшені

Ставтеся до операторської EOA як до облікових даних, а не просто гаманця. Вона тримає токен доступу для кожної мережі, тож ізолюйте її ключ у виділеному підписанті, тримайте на адресі лише робочий запас і газ і не використовуйте її ні для чого іншого.

Нехай оцінка газу буде вашою останньою брамою. Бібліотеки гаманців оцінюють газ перед трансляцією, і невдала оцінка — це реверт, що зринає рано, до того як щось надіслано. Вважайте її пропущеним виконанням і переходьте до наступного кандидата, а не повторюйте наосліп.

Стежте за трьома сигналами. Частка ревертів, що зростає, коли дані пошуку старіють або розмір підходить занадто близько до забезпечення. Вік котирування за кожним виконанням, який має триматися в секундах. І виконані суми зі Swapped проти котированих, що ловить дрейф, перш ніж він коштуватиме вам.

Пошук позицій у продакшені

Живий набір можна отримати двома шляхами. Aqua API — найшвидший старт. Відновлення з подій трастлес і не додає зовнішніх залежностей. Обидва ведуть до того самого потоку котирування й виконання.

Aqua API повертає всі відкриті зараз позиції всіх LP: GET /v1.0/strategies/opened на api.1inch.com/aqua, з курсорною пагінацією до 500 записів на сторінку та фільтрами за мережею і app. Кожен запис містить strategyBytes для Order.decode, а також balance і allowance за кожним токеном для попереднього відбору кандидатів. Запити потребують того самого API-ключа, що й інші API 1inch, його видають у Business portal. Індекс трохи відстає від мережі, тож перед виконанням завжди беріть свіже ончейн-котирування.

Реєстр Aqua, ключовий контракт протоколу, емітує Shipped під час відкриття позиції та Docked під час закриття. Живий набір — це Shipped мінус Docked, відновлений з логів починаючи з блока деплою в кожній мережі. Поля подій лежать у даних лога, а не в topics: фільтруйте за адресою контракту та сигнатурою події, потім декодуйте й зіставляйте на боці клієнта.

Форми подій невеликі. Shipped несе maker, app, strategyHash і повні байти стратегії. Docked несе maker, app і strategyHash. SDK дає типізовані помічники для обох, ShippedEvent і DockedEvent, з декодерами fromLog та константами TOPIC, тож ABI вручну ви не збираєте ніколи.

Відновити живий набір з подійtypescript
import { DockedEvent, ShippedEvent } from '@1inch/aqua-sdk'
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

const AQUA = '0x1111113ccf1426a8e30e2bff5e005d929bf6a90a'
const publicClient = createPublicClient({ chain: mainnet, transport: http() })

// Shipped and Docked carry every field in the log data, none in topics, so
// pull the registry's logs and match on the event signature client-side.
const logs = await publicClient.getLogs({
  address: AQUA,
  fromBlock: 23_000_000n, // the per-chain deployment block, or your last checkpoint
  toBlock: 'latest',
})

const live = new Map<string, ShippedEvent>()
for (const log of logs) {
  if (log.topics[0] === ShippedEvent.TOPIC.toString()) {
    const shipped = ShippedEvent.fromLog(log)
    live.set(shipped.strategyHash.toString(), shipped)
  } else if (log.topics[0] === DockedEvent.TOPIC.toString()) {
    live.delete(DockedEvent.fromLog(log).strategyHash.toString())
  }
}

// live now maps strategyHash -> Shipped payload: maker, app and the strategy
// bytes you decode with Order.decode before quoting.
console.log('open positions:', live.size)

Решту дають ончейн-читання. В’ю rawBalances і safeBalances реєстру показують забезпечення гаманця за позицією, а події Swapped роутера несуть виконані свопи. Тримайте котирування свіжими, а не кешуйте їх.

Розмір виконання починається із забезпечення, а не з котирування. Прочитайте забезпечення позиції через в’ю реєстру, тримайте розмір із запасом усередині нього і візьміть свіже котирування рівно на цей розмір перед самим надсиланням. Поля balance та allowance з API — дешевий перший фільтр за багатьма кандидатами, в’ю та котирування — істина для одного.

FAQ для резолверів

Ні. Перевірка доступу вимагає, щоб джерелом транзакції була EOA з токеном доступу, тому контрактні гаманці та бандлери її не проходять. Надсилайте виконання з верифікованої операторської EOA.

Ні. Функція quote — статичний перегляд живого стану, вона нічого не рухає. Виконання зв’язують лише TakerTraits, надіслані зі свопом: threshold і deadline забезпечуються ончейн.

Газ і більше нічого. Будь-яка непройдена перевірка ревертить усю транзакцію, токени не рухаються з жодного боку.

Верифікація через 1inch Business portal надає токен доступу для кожної мережі. Операторській EOA цей токен потрібен у кожній мережі, де ви виконуєте.

Довідник Aqua API Ендпоінти, параметри та формати відповідей для пошуку позицій і статистики, у Business portal. Aqua SDK і SwapVM SDK Типізовані білдери, декодери та парсери подій для флоу виконання, у репозиторії 1inch/sdks. Онбординг резолверів Кроки верифікації та документація для резолверів на порталі 1inch Business. Whitepaper Aqua Повний дизайн шару спільної ліквідності.

Готові виконувати ліквідність Aqua?

Почніть верифікацію та зв’яжіться з командою через портал 1inch Business.

Відкрити портал Business