Tutorial de Smart Contract para Liquidity Mining em Solidity

Cripto

Tutorial de Smart Contract para Liquidity Mining em Solidity

Luiz Duarte
Escrito por Luiz Duarte em 20/07/2023
Junte-se a mais de 34 mil devs

Entre para minha lista e receba conteúdos exclusivos e com prioridade

Recentemente escrevi um tutorial aqui no blog ensinando a criar um protocolo DeFi bem simples, que permite depositar e sacar tokens. No entanto, todo protocolo DeFi, simples ou complexo, enfrenta um desafio muito grande que é como atrair provedores de liquidez (liquidity providers), ou seja, investidores com dinheiro que queiram depositar no protocolo. Afinal, se o protocolo não tiver capital investido, ele não conseguirá operar.

Para fazer essa atração é obrigatório ter incentivos como compartilhamento de taxas, pagamento de dividendos, etc. Uma dessas técnicas muito populares em contratos inteligentes é a chamada Liquidity Mining ou Mineração de Liquidez. No Liquidity Mining os depositantes recebem recompensas na forma de tokens proporcionalmente ao período e montante depositados no liquidity pool do protocolo. Esses tokens obviamente devem ter algum valor, seja para serem trocados (swap) por outros, negociados em corretoras (exchange) ou serem usados para staking ou governança/poder de voto (em DAOs).

Neste tutorial nós vamos fazer um smart contract para liquidity mining, dando continuidade ao tutorial anterior de protocolo DeFi, que é importante que você tenha feito antes de fazer este.

Além disso, é obrigatório que você já tenha conhecimentos de Solidity pois este é um tutorial intermediário. Se for seu primeiro contato, comece por este aqui.

E por fim, você já deve ter conhecimento de como implementar tokens ERC-20 e já ter um seu, publicado na blockchain. Caso nunca tenha feito um token antes, aprenda aqui.

Vamos lá!

#1 – Liquidity Token

Eu vou começar construindo o token de liquidez que será dado como recompensa a todos provedores de liquidez que deixarem seus tokens depositados em nosso protocolo. Este será um token ERC-20 padrão, usando as bibliotecas da OpenZeppelin para produtividade e segurança. Abaixo o código do arquivo LiquidityToken.sol e a explicação logo na sequência.

Começamos importando as bibliotecas que vamos usar e uma interface que vou criar logo na sequência.

Depois, definimos as variáveis de estado, que serão duas:

  • owner: endereço do dono do contrato;
  • liquidityMining: endereço do contrato de mineração de liquidez;

Esses dois endereços terão privilégios especiais, por isso eu armazeno o owner no constructor do nosso contrato, que será executado automaticamente durante o deploy. Mais tarde, o owner usará a função setLiquidityMining para informar o segundo endereço.

Todas as funções ERC-20 serão fornecidos pela herança do contrato ERC20 da OpenZeppelin, e apenas crio a minha versão personalizada da função mint, que será chamada somente pelo owner ou pelo contrato de liquidity mining para mintar e enviar as recompensas para o usuário que merecer.

Essa função é a única que precisamos ter na nossa interface ILPToken.sol, como abaixo.

Essa segregação via interface é importante para um código bem estruturado e aberto para extensão futura. Você pode agora fazer deploy do seu token para ter o endereço dele para a próxima etapa.

Curso Node.js e MongoDB

#2 – Liquidity Mining

Agora vamos adaptar o contrato Colchao.sol do meu tutorial anterior que no momento está assim:

Vou renomear o contrato e o arquivo para LiquidityMining e fazê-lo herdar de ReentrancyGuard da OpenZeppelin, pois teremos que proteger algumas funções críticas de Reentrancy Attacks (repare nos imports).

Agora, vamos definir as variáveis de ambiente. Além da informação do token que o protocolo vai operar (o seu liquidity pool), precisaremos do token que ele vai dar como recompensa (ILPToken), temos de saber o fator de entrega destas recompensas e também armazenar os checkpoints dos depósitos (já explico).

No constructor do nosso contrato recebemos o endereço do token do liquidity pool (que já tínhamos) e do token de recompensa do liquidity mining (que acabamos de fazer). Usamos estes dois endereços para inicializar as respectivas variáveis de estado que usaremos em diversos momentos do nosso contrato.

Agora falando dos checkpoints, como vai funcionar:

  • toda vez que o usuário fizer um depósito, vamos registrar o número do bloco da transação que ele depositou;
  • se for o primeiro depósito, apenas armazenamos as informações;
  • se for um depósito subsequente, pagamos a recompensa devida e atualizamos o checkpoint, para que as próximas recompensas sejam calculadas com o saldo atualizado;

Desta forma, alteramos nossa função de depósito já existente como abaixo.

Repare como guardei o saldo original antes de atualizá-lo, a fim de pagar as recompensas com o saldo que de fato ficou imobilizado.

Na sequência verifico se é um primeiro depósito, para atualizar o checkpoint, ou se for um subsequente, chamamos a função rewardPayment abaixo.

Nesta função nós calculamos a diferença entre o bloco atual e o bloco original do depósito e usamos esta informação mais o fato de recompensas por bloco e a quantidade de capital depositado para chegar na recompensa total. Recompensa esta que será mintada e enviada para o usuário imediatamente, finalizando com a atualização do checkpoint para que o cálculo futuro seja feito em cima do saldo atualizado.

Não esqueça de usar o function modifier nonReentrant para evitar Reentrancy Attacks aqui.

Curso Beholder
Curso Beholder

#3 – Encerrando o Liquidity Mining

Para finalizar, na função de saque fazemos ajuste semelhante. A vantagem de ter criado uma função rewardPayment é que podemos usá-la aqui.

Repare que a diferença dessa função para a original é:

  • usamos o function modifier nonReentrant;
  • pegamos o saldo original, antes do saque;
  • pagamos as recompensas;

Como a maior parte do trabalho foi feita no passo de depósito, realmente ficou bem simples aqui. Então para complementar, que tal criarmos uma função que estima quanto um usuário tem de recompensas para resgatar?

É praticamente o mesmo algoritmo já existente no rewardPayment, mas aqui sendo uma função view pode ser chamada em um dapp sem incorrer em custos. Muito útil para colocar em um dashboard, por exemplo.

E por fim, que tal uma função que exibe o total depositado no liquidity pool do protocolo?

Isso é bem simples, basta consultar o saldo da conta do contrato LiquidityMining no token ERC20 que está sendo usado na sua operação.

E se quiser aprender a reduzir o consumo de gás nos seus smart contracts, este é o artigo certo.

Até a próxima!

Olá, tudo bem?

O que você achou deste conteúdo? Conte nos comentários.

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *