Para resolvers

Executar swaps contra a liquidez da Aqua: o que o papel de resolver exige, como um fill decorre on-chain e como o cotar e executar com o SwapVM SDK.

10 min de leituraAtualizado em julho de 2026

O que fazem os resolvers na Aqua

Em toda a 1inch Network, os swaps que executam a liquidez da Aqua são encaminhados pelos 1inch Resolvers: criadores de mercado e traders de arbitragem que concluem o processo de verificação da 1inch.

A Aqua em si não faz discovery nem matching. Cota e executa uma posição contra uma contraparte por cada chamada de swap. Encontrar que posição executar, e com que dimensão, é trabalho do resolver: o encaminhamento da 1inch entrega swaps à rede, e os seus próprios sistemas podem chamar os contratos diretamente.

Novo na mecânica subjacente? Comece por como funciona a Aqua.

Antes de começar

  • Verificação de resolver através do portal 1inch Business, que concede o token de acesso por chain
  • O token de acesso na EOA operadora que envia a transação de swap
  • Acesso a um nó na chain alvo, mais os tokens de entrada e o gás para os fills
  • Os endereços canónicos dos contratos, idênticos em todas as chains suportadas e listados abaixo
  • Uma chave de API do Business portal para a via de descoberta com a Aqua API

Endereços dos contratos

A Aqua e o router SwapVM são deployments determinísticos com o mesmo endereço em todas as chains suportadas. Interaja apenas com estes dois contratos. Tudo o resto não é Aqua.

Aqua (registo)
0x1111113ccf1426a8e30e2bff5e005d929bf6a90a
Router SwapVM
0x111111338c5091e8440b67b168bae16a668ac0de

Como decorre um fill

  1. 1

    Obtenha verificação

    Conclua o onboarding de resolver através do portal Business e receba o token de acesso para cada chain onde executa.

  2. 2

    Descubra uma posição

    Obtenha posições abertas na Aqua API, reconstrua o conjunto ativo a partir dos eventos Shipped e Docked, ou receba swaps do encaminhamento da 1inch como resolver da rede.

  3. 3

    Cote mesmo antes de submeter

    Chame a função quote do router numa static call com a ordem descodificada. As cotações leem o suporte vivo da carteira, por isso movem-se quando outros fills aterram.

  4. 4

    Defina os seus limites

    Derive um output mínimo da cotação fresca e uma deadline curta. Uma curva deslocada reverte então em vez de executar a uma taxa pior.

  5. 5

    Faça o swap e verifique

    Envie o swap a partir da EOA que detém o token de acesso, exija um recibo bem-sucedido e leia os montantes executados no evento Swapped.

Quickstart: cotar e executar uma posição

Os exemplos abaixo descobrem uma posição aberta com a Aqua API, pedem um quote e submetem uma execução protegida. Correm em Node 22 com os SDK de TypeScript. Os contratos, os eventos, os SDK e a API chamam strategy a uma posição, por isso o código também.

Instalar os SDKbash
pnpm add @1inch/aqua-sdk @1inch/swap-vm-sdk viem
Descobrir posições abertas com a 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)
Cotar, executar e verificartypescript
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)

Experimente primeiro o ciclo num fork

Tudo o que está acima ensaia-se em segurança num fork local. O anvil da Foundry clona a chain num bloco, por isso obtém os contratos reais e as posições ativas reais sem nada em risco. Com envios sem chave ativados pode agir como qualquer EOA que já detenha o token de acesso e percorrer o ciclo completo, descobrir, cotar, executar, verificar, antes da sua primeira transação real.

Ensaio contra um fork da mainnetbash
# 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

Como um fill é executado on-chain

Um fill é uma única transação para o router SwapVM. O router executa o programa de instruções da posição: primeiro a verificação de acesso, depois a curva de preços, depois os movimentos de tokens. O token de entrada vai da sua EOA para a wallet do LP e o de saída volta para si na mesma transação, enquanto o registo da Aqua atualiza os saldos da posição e emite Pushed e Pulled. Nada fica num pool em momento algum.

É tudo ou nada. Se alguma verificação falhar na execução, token de acesso em falta, threshold não atingido, deadline ultrapassado ou cobertura de wallet insuficiente, toda a transação reverte e nenhum token se move. Um fill revertido custa gás e nada mais.

O router expõe quote e swap, e ambos percorrem o mesmo caminho de preços. Nenhum é uma função view, por isso chame quote através de eth_call como chamada estática: lê o estado em direto, avalia exatamente o swap que lhe passa e não altera nada. Essa pré-visualização não é um compromisso. Só os traits que envia com swap vinculam a execução.

Os TakerTraits levam os seus limites de execução para a transação. É a chain que os impõe, não o SDK. Os que mais vai usar:

exactIn
Que lado fica fixo. Com entrada exata fixa o que envia e limita o que recebe. Com saída exata fixa o que recebe e limita o que envia.
threshold
O próprio limite. Na entrada exata, a saída mínima que o fill tem de entregar. Na saída exata, o teto do que gasta. Um threshold a zero desativa a verificação, por isso derive-o sempre de um quote fresco.
deadline
Uma expiração em segundos unix. Uma transação minerada depois reverte, pelo que um fill preso na mempool não pode executar contra uma curva desatualizada.
customReceiver
Entrega a saída a um endereço diferente da EOA emissora, por exemplo uma tesouraria. A verificação de acesso continua a correr contra a origem da transação.
shouldUnwrap
Receba o token nativo em vez da forma embrulhada quando a saída é um token nativo embrulhado.

Não confie nas suas próprias contas para o resultado. Exija um recibo bem-sucedido e depois descodifique o evento Swapped do router: orderHash, maker, taker, tokenIn, tokenOut, amountIn e amountOut. Esses são os montantes executados, e são eles que a sua contabilidade deve registar.

Modos de falha a tratar

Posição fechada
O LP pode fechar uma posição a qualquer momento, e as leituras de saldo revertem assim que ela desaparece. Uma posição ausente da lista opened da API está fechada, mas entre sondagens o revert on-chain continua a ser a fonte de verdade. Verifique de novo antes de cada execução.
Suporte baixo na carteira
As posições cotam a partir de um saldo de wallet partilhado. Uma execução concorrente pode esvaziá-lo primeiro, e a sua transferência reverte mesmo que o quote parecesse bom. Os campos balance e allowance da API ajudam a pré-filtrar, mas são indexados com atraso. Só um quote on-chain fresco é autoritativo.
Preço deslocado
As cotações envelhecem à medida que os fills aterram e o tempo passa. O output mínimo e a deadline transformam um mau fill num revert limpo.
Token de acesso em falta
Sem a credencial na origem da transação, as posições criadas na dApp revertem o swap antes de a lógica de preços correr.
Endereços não canónicos
Apenas os dois endereços canónicos acima são Aqua. Execuções enviadas para qualquer outro sítio falham as posições ativas. Não há nada a verificar por chain, os deployments são idênticos em todo o lado.

Operar em produção

Trate a EOA operadora como uma credencial, não apenas uma wallet. Ela detém o token de acesso por chain, por isso isole a sua chave num assinante dedicado, mantenha no endereço apenas inventário de trabalho e gás, e nunca a reutilize para outra coisa.

Deixe a estimativa de gás ser o seu último portão. As bibliotecas de wallet estimam o gás antes de difundir, e uma estimativa falhada é o revert a aparecer cedo, antes de algo ser enviado. Trate-a como uma execução saltada e passe ao candidato seguinte em vez de repetir às cegas.

Vigie três sinais. A sua taxa de reverts, que sobe quando os dados de descoberta envelhecem ou o tamanho fica demasiado colado à cobertura. A idade do quote por trás de cada execução, que deve manter-se em segundos. E os montantes executados de Swapped face ao cotado, que apanha a deriva antes de custar.

Descobrir posições em produção

Há duas vias para obter o conjunto ativo. A Aqua API é a forma mais rápida de começar. Reconstruir a partir de eventos é trustless e não acrescenta dependências externas. Ambas alimentam o mesmo fluxo de quote e execução.

A Aqua API lista todas as posições atualmente abertas de todos os LP: GET /v1.0/strategies/opened em api.1inch.com/aqua, com paginação por cursor até 500 itens por página e filtros por chain e app. Cada item traz os strategyBytes que passa a Order.decode, além de balance e allowance por token para pré-filtrar candidatos. Os pedidos exigem a mesma chave de API das outras APIs da 1inch, emitida no Business portal. O índice segue a chain com um ligeiro atraso, por isso peça sempre um quote on-chain fresco antes de executar.

O registo Aqua, o contrato central do protocolo, emite Shipped quando uma posição abre e Docked quando fecha. O conjunto vivo é Shipped menos Docked, reconstruído a partir dos logs desde o bloco de deployment de cada chain. Os campos do evento vivem nos dados do log e não nos topics: filtre por endereço de contrato e assinatura de evento, depois descodifique e faça o match do lado do cliente.

As formas dos eventos são pequenas. Shipped transporta maker, app, strategyHash e os bytes completos da estratégia. Docked transporta maker, app e strategyHash. O SDK inclui auxiliares tipados para ambos, ShippedEvent e DockedEvent, com descodificadores fromLog e constantes TOPIC, por isso nunca escreve a ABI à mão.

Reconstruir o conjunto ativo a partir de eventostypescript
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)

As leituras on-chain dão o resto. As vistas rawBalances e safeBalances do registo reportam a cobertura de wallet por trás de uma posição, e os eventos Swapped do router transportam os swaps executados. Mantenha os quotes frescos em vez de os guardar em cache.

Dimensionar um fill começa pela cobertura, não pelo quote. Leia a cobertura da posição com as vistas do registo, mantenha o seu tamanho bem abaixo, depois peça um quote fresco para esse tamanho exato mesmo antes de submeter. Os campos balance e allowance da API são uma primeira triagem barata sobre muitos candidatos, as vistas e o quote são a verdade para um.

FAQ de resolvers

Não. A verificação de acesso exige que a origem da transação seja a EOA detentora do token de acesso, pelo que wallets de contrato e bundlers não a passam. Envie as execuções a partir da EOA operadora verificada.

Não. A função quote é uma pré-visualização estática do estado em direto e não move nada. Só os TakerTraits enviados com o swap vinculam a execução: o threshold e o deadline são impostos on-chain.

Gás e nada mais. Qualquer verificação falhada reverte toda a transação, pelo que nenhum token se move de nenhum dos lados.

A verificação através do 1inch Business portal concede um token de acesso por chain. A EOA operadora precisa desse token em cada chain onde executa.

Referência da Aqua API Endpoints, parâmetros e formatos de resposta para descoberta de posições e estatísticas, no Business portal. Aqua SDK e SwapVM SDK Builders tipados, descodificadores e parsers de eventos para o fluxo de fill, no repositório 1inch/sdks. Onboarding de resolvers Passos de verificação e documentação de resolvers no portal 1inch Business. White paper da Aqua O design completo da camada de liquidez partilhada.

Pronto para executar a liquidez da Aqua?

Inicie a verificação e fale com a equipa através do portal 1inch Business.

Abrir o portal Business