Você está pronto para mergulhar no mundo da arbitragem de empréstimos relâmpago usando Aave e Hardhat? Se você está procurando alavancar o poder das finanças descentralizadas (DeFi) para lucrar com discrepâncias de preços, você está no lugar certo. Neste tutorial passo a passo, iremos orientá-lo no processo de configuração e execução da arbitragem de empréstimos relâmpago usando o protocolo Aave e o ambiente de desenvolvimento Hardhat.
Pré-requisitos
Antes de embarcarmos nesta emocionante jornada, certifique-se de que possui os seguintes pré-requisitos:
Sólido entendimento de Blockchain e Contratos Inteligentes: Você deve ter um conhecimento sólido da tecnologia blockchain e de como os contratos inteligentes funcionam.
Conhecimento em Ethereum e Hardhat: É essencial estar familiarizado com o Ethereum e o ambiente de desenvolvimento Hardhat. Se você é novo no Hardhat, considere primeiro ler a documentação oficial.
Node.js e npm: Certifique-se de ter o Node.js e o npm (Node Package Manager) instalados em sua máquina.
Agora que temos nossos pré-requisitos em ordem, vamos começar a configurar nosso projeto e mergulhar no fascinante mundo da arbitragem de empréstimos relâmpago!
Configurando o Projeto
Passo 1: Inicialize um Novo Projeto Hardhat
Abra o seu terminal e navegue até o diretório do projeto desejado. Execute os seguintes comandos:
npm install --save-dev hardhat
npx hardhat
Siga as instruções para criar um novo projeto Hardhat. Escolha as configurações padrão para simplificar.
Passo 2: Instalar Dependências
Será necessário instalar algumas dependências adicionais para o nosso projeto. Abra o seu terminal e execute os seguintes comandos:
yarn add --dev @nomiclabs/hardhat-ethers@npm:hardhat-deploy-ethers ethers @nomiclabs/hardhat-etherscan @nomiclabs/hardhat-waffle chai ethereum-waffle hardhat hardhat-contract-sizer hardhat-deploy hardhat-gas-reporter prettier prettier-plugin-solidity solhint solidity-coverage dotenv
yarn add @aave/core-v3
Passo 3: Estrutura do Projeto
O diretório do seu projeto deve agora ter a seguinte estrutura:
- contracts/
- FlashLoanArbitrage.sol
- Dex.sol
- deploy/
- 00-deployDex.js
- 01-deployFlashLoanArbitrage.js
- scripts/
- test/
- hardhat.config.js
- package.json
- README.md
Crie um arquivo .env
, adicione tanto SEPOLIA_RPC_URL
quanto PRIVATE_KEY
com suas credenciais apropriadas da seguinte maneira:
SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/....
PRIVATE_KEY=....
Abra o arquivo hardhat.config.js
e atualize-o com os detalhes a seguir:
require("@nomiclabs/hardhat-waffle")
require("hardhat-deploy")
require("dotenv").config()
/**
* @type import('hardhat/config').HardhatUserConfig
*/
const SEPOLIA_RPC_URL =
process.env.SEPOLIA_RPC_URL || "https://eth-sepolia.g.alchemy.com/v2/YOUR-API-KEY"
const PRIVATE_KEY = process.env.PRIVATE_KEY || "0x"
module.exports = {
defaultNetwork: "hardhat",
networks: {
hardhat: {
// Se você quiser fazer um fork, descomente isso
// forking: {
// url: MAINNET_RPC_URL
// }
chainId: 31337,
},
localhost: {
chainId: 31337,
},
sepolia: {
url: SEPOLIA_RPC_URL,
accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY] : [],
// accounts: {
// mnemonic: MNEMONIC,
// },
saveDeployments: true,
chainId: 11155111,
},
},
namedAccounts: {
deployer: {
default: 0, // por padrão, este será o primeiro endereço como deployer
1: 0, // da mesma forma, na mainnet, será o primeiro endereço como deployer. No entanto, observe que, dependendo de como as redes Hardhat estão configuradas, o endereço 0 em uma rede pode ser diferente em relação a outra.
},
player: {
default: 1,
},
},
solidity: {
compilers: [
{
version: "0.8.7",
},
{
version: "0.8.10",
},
],
},
mocha: {
timeout: 500000, // tempo máximo de 500 segundos para execução de testes
},
}
Além disso, crie um novo arquivo chamado helper-hardhat-config.js
no diretório raiz e adicione os seguintes detalhes necessários, lembrando que estamos usando a rede de teste Sepolia para todos os endereços salvos:
PoolAddressesProvider
,daiAddress
,usdcAddress
Aqui está como esse arquivo deve parecer:
const { ethers } = require('hardhat');
const networkConfig = {
default: {
name: 'hardhat',
},
11155111: {
name: 'sepolia',
PoolAddressesProvider: '0x0496275d34753A48320CA58103d5220d394FF77F',
daiAddress:'0x68194a729C2450ad26072b3D33ADaCbcef39D574',
usdcAddress:'0xda9d4f9b69ac6C22e444eD9aF0CfC043b7a7f53f',
},
};
module.exports = {
networkConfig
}
Após todas as adaptações feitas acima, aqui está como a estrutura do nosso projeto deve parecer:
- contracts/
- FlashLoanArbitrage.sol
- Dex.sol
- deploy/
- 00-deployDex.js
- 01-deployFlashLoanArbitrage.js
- scripts/
- test/
-.env
- hardhat.config.js
-helper-hardhat-config
- package.json
- README.md
Passo 4: Contratos
Neste tutorial, estaremos trabalhando com dois contratos inteligentes:
Dex.sol
: Este contrato simula uma exchange descentralizada onde ocorrem oportunidades de arbitragem.FlashLoanArbitrage.sol
: Este contrato lida com empréstimos relâmpago e operações de arbitragem.
Vamos analisar esses contratos e entender cada linha de código. Primeiro, vamos explorar o FlashLoanArbitrage.sol
.
Dex.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import {IERC20} from "@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20.sol";
contract Dex {
address payable public owner;
IERC20 private dai;
IERC20 private usdc;
// índices de taxa de câmbio
uint256 dexARate = 90;
uint256 dexBRate = 100;
// mantém o controle dos saldos de DAI dos indivíduos
mapping(address => uint256) public daiBalances;
// mantém o controle dos saldos de USDC dos indivíduos
mapping(address => uint256) public usdcBalances;
constructor(address _daiAddress, address _usdcAddress) {
owner = payable(msg.sender);
dai = IERC20(_daiAddress);
usdc = IERC20(_usdcAddress);
}
function depositUSDC(uint256 _amount) external {
usdcBalances[msg.sender] += _amount;
uint256 allowance = usdc.allowance(msg.sender, address(this));
require(allowance >= _amount, "Verifique a autorização do token");
usdc.transferFrom(msg.sender, address(this), _amount);
}
function depositDAI(uint256 _amount) external {
daiBalances[msg.sender] += _amount;
uint256 allowance = dai.allowance(msg.sender, address(this));
require(allowance >= _amount, "Verifique a autorização do token");
dai.transferFrom(msg.sender, address(this), _amount);
}
function buyDAI() external {
uint256 daiToReceive = ((usdcBalances[msg.sender] / dexARate) * 100) *
(10**12);
dai.transfer(msg.sender, daiToReceive);
}
function sellDAI() external {
uint256 usdcToReceive = ((daiBalances[msg.sender] * dexBRate) / 100) /
(10**12);
usdc.transfer(msg.sender, usdcToReceive);
}
function getBalance(address _tokenAddress) external view returns (uint256) {
return IERC20(_tokenAddress).balanceOf(address(this));
}
function withdraw(address _tokenAddress) external onlyOwner {
IERC20 token = IERC20(_tokenAddress);
token.transfer(msg.sender, token.balanceOf(address(this)));
}
modifier onlyOwner() {
require(
msg.sender == owner,
"Somente o proprietário do contrato pode chamar esta função"
);
_;
}
receive() external payable {}
}
Leia a Explicação do Contrato Dex:
O contrato Dex.sol
simula uma bolsa descentralizada. Vamos analisar suas principais características:
Contrato Dex: O contrato principal define variáveis de armazenamento para o proprietário, os endereços de DAI e USDC e instâncias de IERC20 para DAI e USDC.
Depósitos de Tokens: As funções
depositUSDC
edepositDAI
permitem que os usuários depositem tokens USDC e DAI, atualizando seus saldos.Troca de Tokens: As funções
buyDAI
esellDAI
simulam trocas de tokens, comprando DAI com USDC e vendendo DAI por USDC com base nas taxas de câmbio.Acompanhamento de Saldo: O contrato acompanha os saldos individuais dos usuários para DAI e USDC com mapeamentos.
Retirada de Tokens: A função
withdraw
permite que o proprietário do contrato retire tokens do contrato.
FlashLoanArbitrage.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import {FlashLoanSimpleReceiverBase} from "@aave/core-v3/contracts/flashloan/base/FlashLoanSimpleReceiverBase.sol";
import {IPoolAddressesProvider} from "@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol";
import {IERC20} from "@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20.sol";
interface IDex {
function depositUSDC(uint256 _amount) external;
function depositDAI(uint256 _amount) external;
function buyDAI() external;
function sellDAI() external;
}
contract FlashLoanArbitrage is FlashLoanSimpleReceiverBase {
address payable owner;
// Endereço do contrato Dex
address private dexContractAddress =
0x81EA031a86EaD3AfbD1F50CF18b0B16394b1c076;
IERC20 private dai;
IERC20 private usdc;
IDex private dexContract;
constructor(address _addressProvider, address _daiAddress, address _usdcAddress)
FlashLoanSimpleReceiverBase(IPoolAddressesProvider(_addressProvider))
{
owner = payable(msg.sender);
dai = IERC20(_daiAddress);
usdc = IERC20(_usdcAddress);
dexContract = IDex(dexContractAddress);
}
/**
Esta função é chamada após o seu contrato receber a quantia emprestada pelo flash loan
*/
function executeOperation(
address asset,
uint256 amount,
uint256 premium,
address initiator,
bytes calldata params
) external override returns (bool) {
//
// Este contrato agora possui os fundos solicitados.
// Sua lógica vai aqui.
//
// Operação de arbitragem
dexContract.depositUSDC(1000000000); // 1000 USDC
dexContract.buyDAI();
dexContract.depositDAI(dai.balanceOf(address(this)));
dexContract.sellDAI();
// No final da sua lógica acima, este contrato deve
// a quantia emprestada + prêmios.
// Portanto, garanta que seu contrato tenha o suficiente para pagar
// essas quantias.
// Aprova a permissão do contrato da Pool para *retirar* a quantia devida
uint256 amountOwed = amount + premium;
IERC20(asset).approve(address(POOL), amountOwed);
return true;
}
function requestFlashLoan(address _token, uint256 _amount) public {
address receiverAddress = address(this);
address asset = _token;
uint256 amount = _amount;
bytes memory params = "";
uint16 referralCode = 0;
POOL.flashLoanSimple(
receiverAddress,
asset,
amount,
params,
referralCode
);
}
function approveUSDC(uint256 _amount) external returns (bool) {
return usdc.approve(dexContractAddress, _amount);
}
function allowanceUSDC() external view returns (uint256) {
return usdc.allowance(address(this), dexContractAddress);
}
function approveDAI(uint256 _amount) external returns (bool) {
return dai.approve(dexContractAddress, _amount);
}
function allowanceDAI() external view returns (uint256) {
return dai.allowance(address(this), dexContractAddress);
}
function getBalance(address _tokenAddress) external view returns (uint256) {
return IERC20(_tokenAddress).balanceOf(address(this));
}
function withdraw(address _tokenAddress) external onlyOwner {
IERC20 token = IERC20(_tokenAddress);
token.transfer(msg.sender, token.balanceOf(address(this)));
}
modifier onlyOwner() {
require(
msg.sender == owner,
"Somente o proprietário do contrato pode chamar esta função"
);
_;
}
receive() external payable {}
}
Leia a Explicação do Contrato FlashLoanArbitrage:
O contrato FlashLoanArbitrage.sol
é o núcleo de nossa estratégia de arbitragem. Ele utiliza empréstimos relâmpago da Aave para executar negociações lucrativas. Vamos analisar os principais aspectos do contrato:
Imports e Interfaces: Importe os contratos e interfaces necessários da Aave e OpenZeppelin. Isso inclui
FlashLoanSimpleReceiverBase
,IPoolAddressesProvider
eIERC20
.Interface IDex: Define a interface para a exchange descentralizada (DEX) onde ocorre a arbitragem. Métodos como
depositUSDC
,depositDAI
,buyDAI
esellDAI
são definidos.Contrato FlashLoanArbitrage: O contrato principal, herdando de, inicializa endereços e contratos para DAI, USDC e o DEX. Ele implementa a função
executeOperation
que executa a operação de arbitragem, comprando DAI a uma taxa mais baixa e vendendo-o a uma taxa mais alta.Execução de Empréstimo Relâmpago: A operação de arbitragem é executada dentro da função
executeOperation
. Fundos são depositados, DAI é comprado, depositado e depois vendido.Pagamento do Empréstimo: O contrato paga o valor do empréstimo relâmpago mais o prêmio à Aave, aprovando o contrato Pool a retirar a quantia devida.
Solicitação de Empréstimo Relâmpago: A função
requestFlashLoan
inicia o empréstimo relâmpago chamandoflashLoanSimple
do contrato POOL.Aprovação de Token e Permissão: Funções como
approveUSDC
,approveDAI
,allowanceUSDC
eallowanceDAI
são incluídas para aprovar tokens e verificar permissões para a DEX.Consulta de Saldo e Retirada: A função
getBalance
verifica o saldo de um token. A funçãowithdraw
permite que o proprietário do contrato retire tokens.
Passo 5: Implantação de Contratos Inteligentes
Implantar seus contratos inteligentes é o próximo passo crucial. Vamos dar uma olhada nos scripts de implantação.
00-deployDex.js
O script de implantação para o contrato Dex.sol
:
const { network } = require("hardhat")
const { networkConfig } = require("../helper-hardhat-config")
module.exports = async ({ getNamedAccounts, deployments }) => {
const { deploy, log } = deployments
const { deployer } = await getNamedAccounts()
const chainId = network.config.chainId
const arguments = [networkConfig[chainId]["daiAddress"],networkConfig[chainId]["usdcAddress"]]
const dex = await deploy("Dex", {
from: deployer,
args: arguments,
log: true,
})
log("Dex contract deployed at : ", dex.address)
}
module.exports.tags = ["all", "dex"]
01-deployFlashLoanArbitrage.js
O script de implantação para o contrato FlashLoanArbitrage.sol
:
const { network } = require("hardhat")
const { networkConfig } = require("../helper-hardhat-config")
module.exports = async ({ getNamedAccounts, deployments }) => {
const { deploy, log } = deployments
const { deployer } = await getNamedAccounts()
const chainId = network.config.chainId
const arguments = [networkConfig[chainId]["PoolAddressesProvider"],networkConfig[chainId]["daiAddress"],networkConfig[chainId]["usdcAddress"]]
const dex = await deploy("FlashLoanArbitrage", {
from: deployer,
args: arguments,
log: true,
})
log("FlashLoanArbitrage contract deployed at : ", dex.address)
}
module.exports.tags = ["all", "FlashLoanArbitrage"]
Vamos começar implantando o contrato Dex.sol
:
yarn hardhat deploy --tags dex --network sepolia
O endereço do contrato Dex
é "0x81EA031a86EaD3AfbD1F50CF18b0B16394b1c076", que é adicionado ao contrato FlashLoanArbitrage
.
Em seguida, implantamos o contrato FlashLoanArbitrage.sol
executando o comando abaixo:
yarn hardhat deploy --tags FlashLoanArbitrage --network sepolia
Aqui está o endereço do contrato FlashLoanArbitrage
:
"0xc30b671E6d94C62Ee37669b229c7Cd9Eab2f7098"
Passo 5: Testando Contratos Inteligentes
Agora vamos testar os contratos usando o ambiente de desenvolvimento Remix IDE, mas para ser mais específico, aqui está onde a Arbitragem de Empréstimo Relâmpago acontece:
// Índices de Taxa de Câmbio
uint256 dexARate = 90;
uint256 dexBRate = 100;
Aqui, compramos **1 DAI**
a **0.90**
e o vendemos a 100,00. Quando copiar o código para o Remix IDE, considere alterar as importações abaixo em ambos os contratos de acordo:
import {IERC20} from "@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20.sol";
import {FlashLoanSimpleReceiverBase} from "https://github.com/aave/aave-v3-core/blob/master/contracts/flashloan/base/FlashLoanSimpleReceiver.sol";
import {IPoolAddressesProvider} from "https://github.com/aave/aave-v3-core/blob/master/contracts/interfaces/IPoolAddressesProvider.sol";
Adicionando liquidez ao contrato Dex.sol:
**USDC 1500**
**DAI 1500**
Aprovação
**USDC 1000000000
****DAI 1200000000000000000000
**
Solicitação de Empréstimo — USDC (6 casas decimais):
-
**0xda9d4f9b69ac6C22e444eD9aF0CfC043b7a7f53f, 1000000000 // 1.000 USDC
**
Vamos visualizar nossa transação no Etherscan.
Aqui está a explicação das transações:
Transferência de
**1000 USDC
** do contrato AaveLendingPool
para o contratoFlashLoanArbitrage
.Depósito de
**1000 USDC
** do contratoFlashLoanArbitrage
no contratoDex
.Compra de DAI do contrato
Dex
para o contratoFlashLoanArbitrage
.Depósito da quantia de DAI no contrato
Dex
.Transferência de
**1.111,1111 USDC
** do contratoDex
para o contratoFlashLoanArbitrage
.Pagamento de
**1000 USDC
** +**0,05%
** da taxa de empréstimo relâmpago (**1000,5 USDC
**).
Após uma transação bem-sucedida, verificaremos nosso saldo, que aumenta para **110,611100 USDC
**.
Repositório do GitHub: https://github.com/Muhindo-Galien/Aave-Flash-Loan-Arbitrage
Conclusão
Parabéns! Você embarcou em uma jornada emocionante para explorar a arbitragem de empréstimos relâmpago usando Aave e Hardhat. Neste tutorial, você aprendeu a configurar seu projeto, entender contratos inteligentes, implantá-los e testá-los usando o REMIX IDE. Com seu conhecimento recém-adquirido, agora você está preparado para explorar e experimentar mais no mundo das finanças descentralizadas. Feliz codificação e descoberta de oportunidades de arbitragem!
Este artigo foi escrito por Galien Dev e traduzido por Adriano P. de Araujo. O original em inglês pode ser encontrado aqui.
Top comments (0)