Introdução
Os exemplos abaixo são implementações dos dois estilos de swaps (trocas) de caminhos múltiplos disponíveis na v3. Os exemplos abaixo não são códigos prontos para a fase de implantação e são implementados de maneira simplista para fins de aprendizado.
Configurando o contrato
Declare a versão do solidity que será usada para compilar o contrato e o abicoder v2
para permitir que arrays e structs aninhados arbitrariamente sejam codificados e decodificados no recurso calldata, que usamos ao executar um swap.
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
Importe os dois contratos necessários com a instalação do pacote npm.
import '@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol';
import '@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol';
Crie um contrato chamado SwapExamples
e declare uma variável pública imutável swapRouter
do tipo ISwapRouter
. Isso nos permite chamar funções na interface ISwapRouter
.
contract SwapExamples {
// Para o escopo destes exemplos de swap,
// detalharemos as considerações de design ao usar `exactInput`, `exactInputSingle`, `exactOutput` e `exactOutputSingle`.
// Deve-se notar que, por causa desses exemplos, passamos o roteador do swap como um argumento construtor em vez de herdá-lo.
// Contratos de exemplo mais avançados detalharão como herdar o roteador de troca com segurança.
// Este exemplo troca DAI/WETH9 para swaps de caminho único e DAI/USDC/WETH9 para swaps de caminhos múltiplos.
ISwapRouter public immutable swapRouter;
Codifique rigidamente os endereços do contrato do token e os níveis de taxa do pool para o exemplo. Na fase de implantação, você provavelmente usaria um parâmetro de input para isso e passaria o input para uma variável de memória, permitindo que o contrato alterasse os pools e tokens com os quais interage por transação, mas para simplicidade conceitual, estamos codificando-os rigidamente aqui.
address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address public constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
// Para este exemplo, definiremos a taxa do pool para 0.3%.
uint24 public constant poolFee = 3000;
constructor(ISwapRouter _swapRouter) {
swapRouter = _swapRouter;
}
Swaps de Caminhos Múltiplos de Input Exato
Swaps de caminhos múltiplos de input exato trocarão um valor fixo de um determinado token de input pelo valor máximo possível para um determinado output e podem incluir um número arbitrário de swaps intermediários.
Parâmetros de Input
-
path
: O caminho (path) é uma sequência detokenAddress
-fee
(taxa) -tokenAddress
, que são as variáveis necessárias para calcular cada endereço de contrato do pool em nossa sequência de swaps. O código do roteador do swap de caminhos múltiplos encontrará automaticamente o pool correta com essas variáveis e executará a troca necessária dentro de cada pool em nossa sequência. -
recipient
: o endereço de destino do ativo de saída. -
deadline
: o tempo unix após o qual uma transação será revertida, para proteger contra longos atrasos e o aumento da chance de grandes oscilações de preço. -
amountIn
: o valor do ativo de entrada -
amountOutMin
: o valor mínimo do ativo de saída, menor que o que fará com que a transação seja revertida. Para este exemplo, vamos defini-lo como0
. Na fase de implantação será necessário usar o SDK para cotar um preço esperado, ou um oráculo de preço on-chain para sistemas mais avançados e resistentes à manipulação.
Chamando a função
/// @nota swapExactInputMultihop troca um valor fixo de DAI por um valor máximo possível de WETH9 por meio de um pool intermediária.
/// Para este exemplo, trocaremos DAI por USDC e, em seguida, USDC por WETH9 para obter o output desejado.
/// @desenvolvimento O endereço de quem chama deve aprovar este contrato para gastar pelo menos o valor de `amountIn` de suas DAI para que essa função seja bem-sucedida.
/// @parâmetro amountIn A quantidade de DAI a ser trocada.
/// @retornar amountOut O valor de WETH9 recebido após o swap.
function swapExactInputMultihop(uint256 amountIn) external returns (uint256 amountOut) {
// Transferir `amountIn` de DAI para este contrato.
TransferHelper.safeTransferFrom(DAI, msg.sender, address(this), amountIn);
// Aprovar o gasto de DAI pelo roteador.
TransferHelper.safeApprove(DAI, address(swapRouter), amountIn);
// Vários swaps dos pools são codificados por meio de bytes chamados de `path`, que é uma sequência de endereços de tokens e poolFees que definem os pools usadas nas trocas.
// O formato para a codificação do pool é (tokenIn, fee, tokenOut/tokenIn, fee, tokenOut) em que o parâmetro tokenIn/tokenOut é o token compartilhado entre os pools.
// Como estamos trocando DAI por USDC e depois USDC por WETH9, a codificação do caminho é (DAI, 0.3%, USDC, 0.3%, WETH9).
ISwapRouter.ExactInputParams memory params =
ISwapRouter.ExactInputParams({
path: abi.encodePacked(DAI, poolFee, USDC, poolFee, WETH9),
recipient: msg.sender,
deadline: block.timestamp,
amountIn: amountIn,
amountOutMinimum: 0
});
// Isso executa o swap.
amountOut = swapRouter.exactInput(params);
}
Swaps de Caminhos Múltiplos de Output Exato
Um swap de output exato trocará uma quantidade variável do token de input por uma quantidade fixa do token de output. Esta é a técnica menos comum para swaps de caminhos múltiplos. O código para o swap é basicamente o mesmo, exceto por uma diferença notável, o Path
é codificado de trás pra frente, pois uma troca de output exato é executada na ordem inversa para passar as variáveis necessárias para a cadeia de transações.
Parâmetros de Input
-
path
: O caminho é uma sequência detokenAddress
Fee
tokenAddress
, codificado em ordem inversa, que são as variáveis necessárias para calcular cada endereço de contrato de pool em nossa sequência de swaps. O código do roteador do swap de caminhos múltiplos encontrará automaticamente o pool correta com essas variáveis e executará a troca necessária dentro de cada pool em nossa sequência. -
recipient
: o endereço de destino do ativo de saída. -
deadline
: o tempo unix após o qual uma transação será revertida, para proteger contra longos atrasos e o aumento da chance de grandes oscilações de preço. -
amountOut
: A quantidade desejada de WETH9. -
amountInMaximum
: O valor máximo de DAI disposto a ser trocado pelo valor especificado de WETH9.
Chamando a função
/// @nota swapExactOutputMultihop troca um valor mínimo possível de DAI por um valor fixo de WETH por meio de um pool intermediário.
/// Para este exemplo, queremos trocar DAI por WETH9 por meio de um pool de USDC, mas especificamos o valor desejado de WETH9. Observe como a codificação do caminho é ligeiramente diferente para trocas de outputs exatos.
/// @desenvolvimento O endereço de quem chama deve aprovar o gasto de suas DAI pelo contrato para que essa função seja bem-sucedida. Como a quantidade de input de DAI é variável,
/// o endereço de chamada precisará aprovar um valor um pouco maior, antecipando alguma variação.
/// @parâmetro amountOut A quantidade desejada de WETH9.
/// @parâmetro amountInMaximum O valor máximo de DAI disposto a ser trocado pelo amountOut especificado de WETH9.
/// @retornar amountIn O valorIn de DAI realmente gasto para receber o amountOut desejado.
function swapExactOutputMultihop(uint256 amountOut, uint256 amountInMaximum) external returns (uint256 amountIn) {
// Transfira o `amountInMaximum` especificado para este contrato.
TransferHelper.safeTransferFrom(DAI, msg.sender, address(this), amountInMaximum);
// Aprove o gasto de `amountInMaximum` pelo roteador.
TransferHelper.safeApprove(DAI, address(swapRouter), amountInMaximum);
// O caminho do parâmetro é codificado como (tokenOut, fee, tokenIn/tokenOut, fee, tokenIn)
// O campo tokenIn/tokenOut é o token compartilhado entre os dois pools usados no swap de vários pools. Neste caso, USDC é o token "compartilhado".
// Para um swap exactOutput, a primeira troca que ocorre é a troca que retorna o eventual token desejado.
// Nesse caso, nosso token de output desejado é o WETH9, para que a troca ocorra primeiro e seja codificada no caminho de acordo.
ISwapRouter.ExactOutputParams memory params =
ISwapRouter.ExactOutputParams({
path: abi.encodePacked(WETH9, poolFee, USDC, poolFee, DAI),
recipient: msg.sender,
deadline: block.timestamp,
amountOut: amountOut,
amountInMaximum: amountInMaximum
});
// Executa o swap, retornando o valor realmente gasto.
amountIn = swapRouter.exactOutput(params);
// Se o swap não exigir o valor totalInMaximum para atingir o amountOut exato, reembolsamos o msg.sender e aprovamos o gasto de valor 0 pelo roteador.
if (amountIn < amountInMaximum) {
TransferHelper.safeApprove(DAI, address(swapRouter), 0);
TransferHelper.safeTransferFrom(DAI, address(this), msg.sender, amountInMaximum - amountIn);
}
}
Contrato Completo:
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import '@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol';
import '@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol';
contract SwapExamples {
// Para o escopo destes exemplos de swap,
// detalharemos as considerações de design ao usar `exactInput`, `exactInputSingle`, `exactOutput` e `exactOutputSingle`.
// Deve-se notar que, por causa desses exemplos, passamos o roteador do swap como um argumento construtor em vez de herdá-lo.
// Contratos de exemplo mais avançados detalharão como herdar o roteador de troca com segurança.
// Este exemplo troca DAI/WETH9 para swaps de caminho único e DAI/USDC/WETH9 para swaps de caminhos múltiplos.
ISwapRouter public immutable swapRouter;
address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address public constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
// Para este exemplo, definiremos a taxa do pool para 0.3%.
uint24 public constant poolFee = 3000;
constructor(ISwapRouter _swapRouter) {
swapRouter = _swapRouter;
}
/// @nota swapExactInputMultihop troca um valor fixo de DAI por um valor máximo possível de WETH9 por meio de um pool intermediário.
/// Para este exemplo, trocaremos DAI por USDC e, em seguida, USDC por WETH9 para obter o output desejado.
/// @desenvolvimento O endereço de quem chama deve aprovar este contrato para gastar pelo menos o valor de `amountIn` de suas DAI para que essa função seja bem-sucedida.
/// @parâmetro amountIn A quantidade de DAI a ser trocada.
/// @retornar amountOut O valor de WETH9 recebido após o swap.
function swapExactInputMultihop(uint256 amountIn) external returns (uint256 amountOut) {
// Transferir `amountIn` de DAI para este contrato.
TransferHelper.safeTransferFrom(DAI, msg.sender, address(this), amountIn);
// Aprovar o gasto de DAI pelo roteador.
TransferHelper.safeApprove(DAI, address(swapRouter), amountIn);
// Vários swaps dos pools são codificados por meio de bytes chamados de `path`, que é uma sequência de endereços de tokens e poolFees que definem os pools usados nas trocas.
//O formato para a codificação do pool é (tokenIn, fee, tokenOut/tokenIn, fee, tokenOut) em que o parâmetro tokenIn/tokenOut é o token compartilhado entre os pools.
// Como estamos trocando DAI por USDC e depois USDC por WETH9, a codificação do caminho é (DAI, 0.3%, USDC, 0.3%, WETH9).
ISwapRouter.ExactInputParams memory params =
ISwapRouter.ExactInputParams({
path: abi.encodePacked(DAI, poolFee, USDC, poolFee, WETH9),
recipient: msg.sender,
deadline: block.timestamp,
amountIn: amountIn,
amountOutMinimum: 0
});
// Isso executa o swap.
amountOut = swapRouter.exactInput(params);
}
/// @nota swapExactOutputMultihop troca um valor mínimo possível de DAI por um valor fixo de WETH por meio de um pool intermediário.
/// Para este exemplo, queremos trocar DAI por WETH9 por meio de um pool de USDC, mas especificamos o valor desejado de WETH9. Observe como a codificação do caminho é ligeiramente diferente para trocas de outputs exatos.
/// @desenvolvimento O endereço de quem chama deve aprovar o gasto de suas DAI pelo contrato para que essa função seja bem-sucedida. Como a quantidade de input de DAI é variável,
/// o endereço de chamada precisará aprovar um valor um pouco maior, antecipando alguma variação.
/// @parâmetro amountOut A quantidade desejada de WETH9.
/// @parâmetro amountInMaximum O valor máximo de DAI disposto a ser trocado pelo amountOut especificado de WETH9.
/// @retornar amountIn O valorIn de DAI realmente gasto para receber o amountOut desejado.
function swapExactOutputMultihop(uint256 amountOut, uint256 amountInMaximum) external returns (uint256 amountIn) {
// Transfira o `amountInMaximum` especificado para este contrato.
TransferHelper.safeTransferFrom(DAI, msg.sender, address(this), amountInMaximum);
// Aprove o gasto de `amountInMaximum` pelo roteador.
TransferHelper.safeApprove(DAI, address(swapRouter), amountInMaximum);
// O caminho do parâmetro é codificado como (tokenOut, fee, tokenIn/tokenOut, fee, tokenIn)
// O campo tokenIn/tokenOut é o token compartilhado entre os dois pools usados no swap de vários pools. Neste caso, USDC é o token "compartilhado".
// Para um swap exactOutput, a primeira troca que ocorre é a troca que retorna o eventual token desejado.
// Nesse caso, nosso token de output desejado é o WETH9, para que a troca ocorra primeiro e seja codificada no caminho de acordo.
ISwapRouter.ExactOutputParams memory params =
ISwapRouter.ExactOutputParams({
path: abi.encodePacked(WETH9, poolFee, USDC, poolFee, DAI),
recipient: msg.sender,
deadline: block.timestamp,
amountOut: amountOut,
amountInMaximum: amountInMaximum
});
// Executa o swap, retornando o valor realmente gasto.
amountIn = swapRouter.exactOutput(params);
// Se o swap não exigir o valor totalInMaximum para atingir o amountOut exato, reembolsamos o msg.sender e aprovamos o gasto de valor 0 pelo roteador.
if (amountIn < amountInMaximum) {
TransferHelper.safeApprove(DAI, address(swapRouter), 0);
TransferHelper.safeTransferFrom(DAI, address(this), msg.sender, amountInMaximum - amountIn);
}
}
Este guia foi publicado no Uniswap Docs. Tradução por Paulinho Giovannini.
Top comments (1)
Parabéns pelo conteúdo!