WEB3DEV

Cover image for Empréstimos Relâmpago, The Graph e Arbitragem Triangular: Identificando Oportunidades como Tony Soprano, Parte II
Paulo Gio
Paulo Gio

Posted on

Empréstimos Relâmpago, The Graph e Arbitragem Triangular: Identificando Oportunidades como Tony Soprano, Parte II

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*gKju8NLzVMkb0G2MWqLXAw.jpeg

Bem-vindo à Parte II de nossa série sobre a descoberta de oportunidades lucrativas de arbitragem! No último artigo, entramos em detalhes sobre a identificação de oportunidades de arbitragem triangular usando o The Graph, no entanto, isso foi apenas metade da batalha. Para ter sucesso, também devemos avaliar cada negociação potencial para determinar a rentabilidade. Como disse Tony Soprano, “Não se trata de quanto você ganha, mas de quanto você guarda”. E é exatamente isso que vamos explorar neste artigo — como avaliar oportunidades potenciais de arbitragem e garantir a máxima rentabilidade.

O que vamos abordar:

  • Consultas GraphQL ao The Graph
  • Matemática do CFMM (Formador de Mercado de Função Constante) da Uniswap V3
  • Interação de Contrato Inteligente com ABIs (Interface Binária de Aplicativo)

Continuando de onde paramos

Em nossa postagem anterior, usamos o The Graph para indexar dados de pool de liquidez da rede principal da Ethereum (Mainnet) e criamos um algoritmo para identificar caminhos de negociação. Agora, nosso próximo passo é determinar a rentabilidade de cada caminho. Mas como podemos fazer isso?

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*7UM-f1l2sfUAaXOVu64GLg.png

Negociação mapeada

Dividimos nosso processo em algumas etapas:

  • Calcular diferenças percentuais de preço;
  • Procurar pools de liquidez (LPs) ideais para tomar emprestados ativos (Pools de Empréstimos);
  • Determinar a quantidade de entrada de token ideal;
  • Verificar a rentabilidade com uma verificação na cadeia.

I. Diferenças percentuais de preço

Na arbitragem triangular, buscamos capitalizar as discrepâncias nas taxas de token entre três pools de liquidez para gerar lucros. Para detectar tais oportunidades, usamos diferenças percentuais de preço (PPD) como uma métrica chave, que envolve a identificação de variações das exchanges entre os três LPs.

Focar nos caminhos que oferecem uma PPD favorável traz dois benefícios principais. Primeiro, agiliza nosso processo de busca, ao restringir os caminhos potencialmente lucrativos, reduzindo assim a sobrecarga computacional. Segundo, nos permite identificar o caminho mais lucrativo a ser tomado para maximizar nossos lucros potenciais.

A. Diferenças percentuais de preço: ponto de partida

check_all_structured_paths() — itera sobre nosso array de objetos mapeados e passa cada caminho para find_most_profitable_permutation(). Esta função calcula a diferença percentual de preço mais rentável para o caminho dado. Se a PPD exceder nosso limite especificado, adicionamos o caminho à nossa lista de candidatos em potencial.

function check_all_structured_paths(paths) {
 const inital_check_profitable_paths = [];

 for (const { path, token_ids, pool_addresses } of paths) {
   const most_profitable_permutation = find_most_profitable_permutation(path);

   if (
     most_profitable_permutation.price_percentage_difference >
     PRICE_PERCENTAGE_DIFFERENCE_THRESHOLD
   ) {
     most_profitable_permutation.token_ids = token_ids;

     most_profitable_permutation.pool_addresses = pool_addresses;

     inital_check_profitable_paths.push(most_profitable_permutation);
   }
 }
 return inital_check_profitable_paths;
}
Enter fullscreen mode Exit fullscreen mode

B. Diferenças percentuais de preço: permutações e cálculos

function find_most_profitable_permutation(path) {
 function find_permutations(path_array, temp) {
   let current;
   if (!path_array.length) {
     const calculated_path_and_difference =
       calculate_percentage_difference_of_path(temp);

     all_permutations_for_order =
       all_permutations_for_order.price_percentage_difference <
       calculated_path_and_difference.price_percentage_difference
         ? calculated_path_and_difference
         : all_permutations_for_order;
   }
   for (let i = 0; i < path_array.length; i++) {
     current = path_array.splice(i, 1)[0];
     find_permutations(path_array, temp.concat(current));
     path_array.splice(i, 0, current);
   }
 }

 let all_permutations_for_order = {
   price_percentage_difference: 0,
 };
 find_permutations(path, []);

 return all_permutations_for_order;
}
Enter fullscreen mode Exit fullscreen mode

find_most_profitable_permutation( ) — gera todas as permutações possíveis do nosso caminho. Matematicamente, como existem seis variações potenciais, calculamos cada uma e identificamos a mais lucrativa. Para fazer isso, começamos com um ponto base de 0 e pesamos os resultados de cada permutação em cada iteração. Uma vez que o processo de permutação está completo, a função retorna um objeto que inclui a diferença percentual de preço calculada para sua permutação mais otimizada.

https://miro.medium.com/v2/resize:fit:1100/0*nH7jdR9UDbt-uiQU

calculate_percentage_difference_of_path( ) — percorre nosso caminho e calcula cada negociação usando os dados fornecidos pelo pool de liquidez atual. Para fazer isso, começamos com uma quantidade de entrada base de ΔY = 1 e a atualizamos com base nos resultados do método de cálculo aplicado.

function calculate_percentage_difference_of_path(path) {

 verfiy_token_path(path);

 let arbitrary_amount = 1;

 for (const liqudity_pool of path) {

   if (liqudity_pool.exchange === 'uniswapV3') {
     //calcule o valor do token com uniswap v3
     arbitrary_amount = uniswap_V3_swap_math(liqudity_pool, arbitrary_amount);
   } else {
     //calcule o valor do token com uniswap v2 e sushiswap
     arbitrary_amount = uniswap_V2_sushiswap_swap_math(
       liqudity_pool,
       arbitrary_amount
     );
   }
 }

 const starting_price = 1;

 const current_price = arbitrary_amount;

 const absoluteDifference = current_price - starting_price;

 const average = (current_price + starting_price) / 2;

 const price_percentage_difference = (absoluteDifference / average) * 100;

 return {
   price_percentage_difference: price_percentage_difference,
   path: path,
 };
}
Enter fullscreen mode Exit fullscreen mode

Os métodos de cálculo em cada iteração são determinados pela exchange que hospeda o pool de liquidez. Lembre-se de nosso artigo anterior ilustrando as diferenças entre nossas DEXs e como cada fórmula determinaria o valor resultante de uma negociação.

Método Um: Uniswap V2 e Sushiswap

uniswap_V2_sushiswap_swap_math( ) — calcula o valor final resultante de uma negociação ao aproveitar os dados de reservas de token do pool de liquidez.

function uniswap_V2_sushiswap_swap_math(pool, amount) {
  const token_in_reserves =
    pool.token_in === pool.token0.id
      ? Number(pool.reserve0)
      : Number(pool.reserve1);

  const token_out_reserves =
    pool.token_out === pool.token0.id
      ? Number(pool.reserve0)
      : Number(pool.reserve1);

   const calculated_amount = Math.abs(
     (token_in_reserves * token_out_reserves) / (token_in_reserves + amount) -
       token_out_reserves
   );

   return calculated_amount;
}
Enter fullscreen mode Exit fullscreen mode

Determinamos as reservas de tokens de entrada e saída usando os UUIDs de token de entrada e saída, que são estabelecidos por meio do verify_token_path( ). Em seguida, levamos em consideração o estado atual da nossa quantidade negociada e inserimos cada uma dessas variáveis para calcular a quantidade negociada atualizada.

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*86UJG41dDVE1Iy_bTgXWMQ.png

Método Dois: Uniswap V3

uniswap_V3_swap_math( ) — determina o valor final resultante de uma negociação utilizando tanto a liquidez atual quanto a raiz quadrada do preço do pool de liquidez.

function uniswap_V3_swap_math(pool, amount) {
 const token_0 = pool.token_in === pool.token0.id;
 const q96 = 2 ** 96;
 const token_0_decimals = 10 ** Number(pool.token0.decimals);
 const token_1_decimals = 10 ** Number(pool.token1.decimals);
 const liquidty = Number(pool.liquidity);
 const current_sqrt_price = Number(pool.sqrtPrice);

 function calc_amount0(liq, pa, pb) {
   if (pa > pb) {
     [pa, pb] = [pb, pa];
   }
   return Number((liq * q96 * (pb - pa)) / pb / pa);
 }

 function calc_amount1(liq, pa, pb) {
   if (pa > pb) {
     [pa, pb] = [pb, pa];
   }
   return Number((liq * (pb - pa)) / q96);
 }

 if (token_0) {

   const amount_in = amount * token_0_decimals;

   const price_next =
     (liquidty * q96 * current_sqrt_price) /
     (liquidty * q96 + amount_in * current_sqrt_price);

   const output = calc_amount1(
     liquidty,
     price_next,
     Number(current_sqrt_price)
   );

   return output / token_1_decimals;

 } else {

   const amount_in = amount * token_1_decimals;

   const price_diff = (amount_in * q96) / liquidty;

   const price_next = price_diff + current_sqrt_price;

   const output = calc_amount0(
     liquidty,
     price_next,
     Number(current_sqrt_price)
   );
   return output / token_0_decimals;
 }
}
Enter fullscreen mode Exit fullscreen mode

Observação: o uso de um tipo de dados BigInt( ) é recomendado para garantir cálculos precisos da quantidade de saída. Por favor, tenha em mente que as fórmulas fornecidas aqui são uma visão geral básica.

Dependendo se estamos vendendo o token 0 ou o token 1, este método utiliza duas abordagens diferentes para determinar a quantidade de saída da negociação. No entanto, ambas as abordagens seguem o mesmo conjunto de diretrizes, que incluem encontrar a mudança de valor, determinar a raiz quadrada do preço-alvo e calcular a quantidade de saída do token correspondente.

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*33FqQuKtkBWM-vl7zci4PA.png

Matemática Uniswap V3

Depois de termos iterado sobre cada pool de liquidez e atualizado a nossa quantidade negociada, calculamos a diferença percentual de preço entre a nossa quantidade inicial ΔY e a nossa quantidade final ΔX, usando uma fórmula simples para calcular diferenças percentuais de preço.

https://miro.medium.com/v2/resize:fit:1100/0*LZ8j95WuqQbJqe85

PPD

Finalmente, depois de termos calculado cada permutação, a mais lucrativa é retornada junto com a sua diferença percentual de preço da permutação dada.

II. The Graph: encontrando nossos pools de empréstimo

Para integrar com sucesso os empréstimos relâmpago, devemos encontrar credores ideais com liquidez suficiente e taxas de troca baixas. Podemos construir consultas com parâmetros específicos para encontrar tais pools com base nos esquemas de nossos subgrafos. A Uniswap V3 é a melhor opção devido à sua liquidez concentrada, mas também podemos usar a Uniswap V2 e a Sushiswap como alternativas. Usamos duas compilações GraphQL para retornar um único pool de liquidez que atende aos nossos padrões e obter o UUID dos LPs.

{
  pairs(first: 1, orderBy: reserveUSD, orderDirection: desc, where :{${token_number}:"${tokenID}", id_not_in: ["${poolAddress}", "${poolAddress2}", "${poolAddress3}"], reserveUSD_gt: 0}) {
   id
   reserve0
   reserve1
   reserveUSD
   token0Price
   token1Price
   token0 {
     symbol
     id
     decimals
     derivedETH
   }
   token1 {
     symbol
     id
     decimals
     derivedETH
   }
  }
 }
Enter fullscreen mode Exit fullscreen mode

Aqui encontramos pools de liquidez na Uniswap V2 e Sushiswap com o maior valor da métrica "reserveUSD" (a métrica "reserveUSD" é usada para acompanhar o valor total dos ativos mantidos em um pool de liquidez, convertendo todos os ativos do pool em seu valor equivalente em dólares americanos) contendo o UUID do token inserido. Como a taxa de troca para essas DEXs é fixa em 3%, essas consultas visam exclusivamente pools com liquidez suficiente.

{
  token(id: "${tokenID}"){
   whitelistPools(first: 1, orderBy: liquidity, orderDirection: asc, where: {liquidity_gt: "0", tick_gt: 0, id_not_in: ["${poolAddress1}", "${poolAddress2}", "${poolAddress3}"], feeTier_lte: "3000"}){
     token0 {
       symbol
       decimals
       id
       derivedETH
     }
     token0Price
     token1Price
     token1 {
       symbol
       decimals
       id
       derivedETH
     }
     feeTier
     liquidity
     tick
     sqrtPrice
     id
   }
  }
 }
Enter fullscreen mode Exit fullscreen mode

Nossa segunda consulta é exclusiva para a Uniswap V3 e visa encontrar pools de liquidez com taxas de troca baixas que variam de 3% a 0,01%. Também filtramos pools com baixa liquidez juntamente com esses parâmetros.

Usamos o UUID obtido para encontrar o evento de troca mais recente para o pool de liquidez, o que nos ajuda a calcular as avaliações atuais dos tokens (incluindo avaliações em USD) e a construir uma biblioteca de pools de liquidez para o nosso método de determinação de rentabilidade.

// ---- consulta uniswap V2 e sushiswap -----//
{
 {
   swaps(first: 1, orderBy: timestamp, orderDirection: desc, where: { pair: "${address}" } ) {
     pair {
       token0 {
         symbol
         id
         decimals
       }
       token1 {
         symbol
         id
         decimals
       }
       token0Price
       token1Price
       reserve0
       reserve1
       id
     }
     amount0In
     amount0Out
     amount1In
     amount1Out
     amountUSD
     to
     timestamp
   }
 }
}
// ---- consulta uniswap V3 -----//

{
   swaps(first: 1, orderBy: timestamp, orderDirection: desc, where: {pool: "${address}"}){
     sqrtPriceX96
     amountUSD
     timestamp
     id
     amount0
     amount1
     pool {
       token0{
         symbol
         decimals
         id
         derivedETH
       }
       token0Price
       token1Price
       token1 {
         symbol
         decimals
         id
         derivedETH
       }
       feeTier
       liquidity
       tick
       sqrtPrice
       id
     }
   }
 }
Enter fullscreen mode Exit fullscreen mode

get_loan_pool_for_token( ) — determina a ordem em que procuramos por pools de empréstimo em diferentes exchanges. Isso é feito chamando as funções de subgrafo relevantes em uma ordem especificada.

async function get_loan_pool_for_token(tokenId, poolAddresses) {
 try {
   const [poolAddress1, poolAddress2, poolAddress3] = poolAddresses;

   const uniswap_v3_loan_pool_id = await find_most_profitable_loan_pool_V3(
     tokenId,
     poolAddress1,
     poolAddress2,
     poolAddress3
   );

   if (uniswap_v3_loan_pool_id) {
     return await get_uniswap_v3_last_swap_information(
       uniswap_v3_loan_pool_id
     );
   }

   const uniswap_v2_loan_pool_id = await find_most_profitable_loan_pool_V2(
     tokenId,
     poolAddress1,
     poolAddress2,
     poolAddress3
   );
   if(uniswap_v2_loan_pool_id){
     return await get_most_recent_swap_activity_uniswapV2(
       uniswap_v2_loan_pool_id
     );
   }
   const sushi_swap_loan_pool_id =
     await find_most_profitable_loan_pool_sushi_swap(
       tokenId,
       poolAddress1,
       poolAddress2,
       poolAddress3
     );

   if(sushi_swap_loan_pool_id) {
     return await get_most_recent_swap_activity_sushiswap(
       sushi_swap_loan_pool_id
     );
   }

 } catch (error) {
   console.error(error);
 }
}
Enter fullscreen mode Exit fullscreen mode

Quando não conseguimos localizar nenhum pool de empréstimo para um token específico em nossos subgrafos DEX, optamos por excluí-lo da consideração. No entanto, essa decisão carrega o risco de potencialmente perder permutações lucrativas que possam surgir no futuro.

III. Determinando a Rentabilidade

O processo de avaliar a rentabilidade do nosso caminho consiste em duas verificações distintas. A primeira é uma verificação fora da cadeia, onde determinamos a quantidade ideal de token de entrada e a permutação mais lucrativa do caminho. A segunda é uma verificação na cadeia que utiliza a ABI dos pools de liquidez para simular uma negociação usando dados de mercado atuais.

async function profitablity_checks(mapped_paths) {

 const ordered_profitable_path_with_optimal_input_amount = [];

 const proftibale_paths_to_stage_for_smart_contract = [];

  /*
  * verificação fora da cadeia
  */
 for (const path of mapped_paths) {

   off_chain_check(path);

   if (path.profit_usd > MIN_PROFIT_TO_CONSIDER_FOR_ON_CHAIN_CALL) {
     ordered_profitable_path_with_optimal_input_amount.push(path);
   }
 }
  /*
  * Verificação na cadeia. Nosso último ciclo de verificação
  */
 for (const final_path of ordered_profitable_path_with_optimal_input_amount) {

   await on_chain_check(final_path);

   if (
     final_path.profit_usd_onchain_check >
     MIN_PROFIT_TO_CONSIDER_FOR_ON_CHAIN_CALL
   ) {
     proftibale_paths_to_stage_for_smart_contract.push(final_path);
   }
 }
 return proftibale_paths_to_stage_for_smart_contract;
}
Enter fullscreen mode Exit fullscreen mode

Uma vez que o caminho passa por ambas as verificações, podemos prosseguir para prepará-lo para o nosso contrato inteligente e a execução do empréstimo relâmpago.

Determinando a Rentabilidade: Quantidade Ideal de Token de Entrada

off_chain_check( ) – usa um método de iteração recursiva, semelhante à nossa abordagem de diferença percentual de preço, para calcular cada negociação com base na exchange do pool de liquidez para cada permutação do caminho. No entanto, essa função difere no modo como determina a quantidade de entrada ideal, calcula cada negociação com base nas taxas de troca e avalia a rentabilidade com base na liquidez atual.

Existem várias maneiras de determinar a quantidade de entrada ideal. Uma possibilidade é usar uma abordagem matemática para o nosso caminho cíclico.

https://miro.medium.com/v2/resize:fit:1100/0*-DP5iJ-SiInzxkH4

Se somos capazes de calcular os resultados de negociação usando reservas de tokens, podemos determinar a entrada mais ideal ao iterar sobre o nosso caminho e ao calcular a derivada da nossa quantidade de entrada.

https://miro.medium.com/v2/resize:fit:640/0*GX8z2iH6ZCIbqRTB

O nosso método, em contraste, emprega uma abordagem de força bruta. Usamos o generate_optimal_token_input_amounts( ) para produzir um array de montantes emprestados ao calcular a avaliação dos ativos emprestados do nosso pool de empréstimo.

function generate_optimal_token_input_amounts(borrow_token_usd_price) {

 const token_input_amounts = [];

 for (
   let i = MIN_USD_VALUE_FOR_OPTIMAL_INPUT_RANGE;
   i <= MAX_USD_VALUE_FOR_OPTIMAL_INPUT_RANGE;
   i += STEP_BETWEEN_RANGE
 ) {

   const amount_in = i / borrow_token_usd_price;

   token_input_amounts.push([amount_in, i]);

 }

 return token_input_amounts;
}
Enter fullscreen mode Exit fullscreen mode

Após obtermos nosso array de ativos emprestados, utilizamos o calculate_permutation_and_find_optimal_input_token_amount( ) para iterar através de cada quantidade emprestada e calcular a permutação atual do caminho. Neste processo, simulamos a negociação usando os mesmos métodos de cálculo que em nosso método PPD (diferença percentual de preço), enquanto também consideramos as taxas de negociação retidas por cada pool de liquidez.

function calculate_permutation_and_find_optimal_input_token_amount(
 permutation_path,
 loan_pools
) {
 try {
   verfiy_token_path(permutation_path);

   const loan_pool = loan_pools[permutation_path[0].token_in];

   const calcuations = {
     optimal_amount: 0,
     profit: 0,
     usd_input_amount: 0,
     starting_amount: 0,
     path: permutation_path,
     enough_liquidity: true,
   };
   if (loan_pool) {
     const borrow_token_usd_price =
       permutation_path[0].token_in === loan_pool.token0.id
         ? loan_pool.token_0_usd_price
         : loan_pool.token_1_usd_price;

     const loan_fee = 1 - FEE_TEIR_PERCENTAGE_OBJECT[loan_pool.feeTier];

     const amounts = generate_optimal_token_input_amounts(
       borrow_token_usd_price
     );
     for (const [starting_amount, usd_input_amount] of amounts) {

       calcuations.enough_liquidity = true;

       let input_amount = starting_amount * loan_fee;

       const [pool_1, pool_2, pool_3] = permutation_path;

       input_amount = calculate_new_token_amount(
         pool_1,
         input_amount,
         calcuations
       );

       input_amount = calculate_new_token_amount(
         pool_2,
         input_amount,
         calcuations
       );
       input_amount = calculate_new_token_amount(
         pool_3,
         input_amount,
         calcuations
       );
       const profit =
         (input_amount - starting_amount) * borrow_token_usd_price;

       if (!calcuations.enough_liquidity) {
         return;
       }
       if (calcuations.profit === 0) {
         calcuations.profit = profit;
         calcuations.optimal_amount = starting_amount;
         calcuations.usd_input_amount = usd_input_amount;
       }

       if (calcuations.profit < profit) {
         calcuations.profit = profit;
         calcuations.optimal_amount = starting_amount;
         calcuations.usd_input_amount = usd_input_amount;
       } else if (calcuations.profit > profit) {
         return calcuations;
       }
     }
   }
 } catch (error) {
   console.error(error);
 }
}
Enter fullscreen mode Exit fullscreen mode

Durante o ciclo de iteração, se em algum momento a liquidez fornecida por um dos nossos pools cair abaixo da nossa quantidade resultante, as iterações para a permutação atual terminam. O resultado retornado indica que a permutação de negociação não tem liquidez suficiente, desde que cálculos anteriores não tenham mostrado lucro.

Determinando a Rentabilidade: Última Verificação na Cadeia

Na etapa final do nosso processo, fazemos uso das interfaces binárias de aplicação de contratos (ABIs) dos pools de liquidez. A ABI define os métodos e variáveis contidos em um contrato inteligente, permitindo-nos interagir com o contrato e acessar suas funcionalidades. Ao interagir com a ABI, podemos realizar funções que nos permitem executar negociações e ler dados na cadeia.

Então, como acessamos as funcionalidades de um contrato inteligente de um pool de liquidez?

Existem vários serviços disponíveis, como Infura, Alchemy e Kaleido. Esses serviços oferecem contas gratuitas com um limite de solicitações. Uma vez que tenhamos configurado uma conta e obtido uma chave de API, podemos usar bibliotecas como ethers.js ou web3 para acessar as ABIs de contratos inteligentes e interagir com suas funções.

const INFURA_URL_VETTING_KEY =

const provider = new ethers.providers.JsonRpcProvider(INFURA_URL_VETTING_KEY);

// Os endereços do roteador são específicos para a DEX, você pode encontrar esses endereços com uma simples pesquisa no Google.

const uniswap_V3_contract = new ethers.Contract(
     ROUTER_ADDRESS_V3,
     abi,
     provider
   );

const uniswap_V2_contract = new ethers.Contract(
     ROUTER_ADDRESS_V2,
     abi,
     provider
   );

const sushiswap_contract = new ethers.Contract(
     ROUTER_ADDRESS_SUSHISWAP,
     abi,
     provider
   );
Enter fullscreen mode Exit fullscreen mode

O objetivo das nossas chamadas de dados na cadeia é semelhante aos nossos métodos anteriores - queremos simular uma negociação para estimar seu valor. No entanto, neste caso, estamos usando dados em tempo real para melhorar a precisão dos nossos cálculos.

Dependemos principalmente de duas funções estáticas para atingir esse objetivo. Essas funções fornecem uma maneira precisa e contínua de estimar o valor da nossa negociação mapeada. Ao usar dados em tempo real da blockchain, podemos tomar decisões mais informadas e executar negociações de maneira mais eficiente. Essa abordagem é comumente usada em exchanges descentralizadas e outros sistemas de formação de mercado automatizados para facilitar negociações com base nas condições de mercado em tempo real.

quoteExactInputSingle( ) — específico para a Uniswap V3, é usado para cotar o preço de uma negociação que envolve um único ativo de entrada. Ele leva em consideração o fator de taxa para o pool, bem como um limite de preço de raiz quadrada que pode ser usado para especificar a faixa de preço para a negociação.

// ---- Chamada de função ABI Uniswap V3 ---- //

function quoteExactInputSingle(
   address tokenIn,
   address tokenOut,
   uint24 fee,
   uint256 amountIn,
   uint160 sqrtPriceLimitX96
 ) public returns (uint256 amountOut)
Enter fullscreen mode Exit fullscreen mode

getAmountsOut( ) — é exclusivo para a Uniswap V2 e para a Sushiswap. Ele usa seus próprios métodos internos de contrato — getReserves( ) e getAmountOut( ) — para calcular as quantidades máximas de tokens de saída para cada etapa subsequente no caminho pré-definido.

// ---- Chamada de função ABI Uniswap V2 and Sushiswap ---- //

function getAmountsOut(
 uint amountIn,
 address[] memory path
) public view returns (uint[] memory amounts);
Enter fullscreen mode Exit fullscreen mode

O que vem a seguir?

Exploramos o processo de identificar oportunidades lucrativas de arbitragem triangular e utilizar protocolos como o The Graph para indexar a rede principal da Ethereum para dados relevantes. Também nos aprofundamos na aplicação da matemática CFMM em nosso script para descobrir tais oportunidades. Tendo descoberto uma oportunidade viável de arbitragem triangular, podemos agora passar para a seção final, que se concentra na construção de um Contrato Inteligente e na implementação de empréstimos relâmpago para executar as negociações (em breve).

Obtenha uma visão completa do estado atual de nossa construção: repositório GitHub

Fontes e Leituras Adicionais

Artigo original publicado por Brennan Skinner. Traduzido por Paulinho Giovannini.

Top comments (0)