Pular para o conteúdo principal

Criar e promover um Aplicativo DeFi

soliditydefiweb3truffleganacheContratos Inteligentes
Intermediário
strykerin
github.com(opens in a new tab)
31 de dezembro de 2020
11 minutos de leitura minute read

Neste tutorial, construiremos um aplicativo DeFi com Solidity onde os usuários podem depositar um token ERC20 no contrato inteligente e ele cunhará e transferirá os Tokens Farm para eles. Os usuários podem posteriormente retirar seus tokens ERC20 queimando seu Farm Token em contrato inteligente e os tokens ERC20 serão transferidos de volta para eles.

Instale o Truffle e o Ganache

Se esta for a primeira vez que você está escrevendo um contrato inteligente, você precisará configurar seu ambiente primeiro. Vamos usar duas ferramentas:Truffle(opens in a new tab) and Ganache(opens in a new tab).

O Truffle é um ambiente de desenvolvimento e estrutura de teste para o desenvolvimento de contratos inteligentes para o Ethereum. Com o Truffle, é fácil construir e implantar contratos inteligentes na blockchain. O Ganache nos permite criar uma blockchain Ethereum local para testar contratos inteligentes. Ele simula os recursos da rede real e as primeiras 10 contas são financiadas com 100 ether de teste, tornando a implantação e o teste do contrato inteligente gratuitos e fáceis. O Ganache está disponível como um aplicativo de desktop e uma ferramenta de linha de comandos. Para este artigo, usaremos o aplicativo de desktop de interface do usuário.

Aplicativo de área de trabalho Ganache UI(opens in a new tab)Aplicativo de desktop Ganache UI

Para criar o projeto, execute os seguintes passos

mkdir your-project-name
cd your-project-name
truffle init

Isso criará um projeto em branco para o desenvolvimento e implantação de nossos contratos inteligentes. A estrutura do projeto criada é a seguinte:

  • A Pasta para os contratos inteligentes de solidez: contracts

  • migrações: Pasta para os scripts de implantação

  • test: Pasta para testar nossos contratos inteligentes

  • truffle-config.js: Arquivo de configuração do Truffle

Criar o token ERC20

Primeiro, precisamos criar seu token ERC20 que usaremos para apostar no contrato inteligente. Para criar nosso token fungível, primeiro precisamos instalar a biblioteca OpenZeppelin. Esta biblioteca contém as implementações de padrões como o ERC20 e o ERC721. Para instalá-lo, execute os passos:

npm install @openzeppelin/contracts

Usando a biblioteca OpenZeppelin, podemos criar nosso token ERC20 gravando em contracts/MyToken.sol com o seguinte código solidity:

1pragma solidity ^0.8.0;
2
3import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
4
5contract MyToken is ERC20 {
6 constructor() public ERC20("MyToken", "MTKN"){
7 _mint(msg.sender, 1000000000000000000000000);
8 }
9}
Exibir tudo
Copiar

No código acima em:

  • Linha 3: Importamos o contrato ERC20.sol do openzeppelin que contém a implementação para este padrão de token.

  • Linha 5: Herdamos do contrato ERC20.sol.

  • Linha 6: Estamos chamando o construtor ERC20.sol e passando os parâmetros name e symbol como "MyToken" e "MTKN" respectivamente.

  • Linha 7: Estamos cunhando e transferindo 1 milhão de tokens para a conta que está implantando o contrato inteligente (estamos usando os 18 decimais padrão para o token ERC20, o que significa que, se quisermos cunhar 1 token, você o representará como 1000000000000000000, 1 com 18 zeros).

Podemos ver abaixo a implementação do construtor ERC20.sol onde o campo _decimals está definido como 18:

1string private _name;
2string private _symbol;
3uint8 private _decimals;
4
5constructor (string memory name_, string memory symbol_) public {
6 _name = name_;
7 _symbol = symbol_;
8 _decimals = 18;
9}
Exibir tudo
Copiar

Compilar o token ERC20

Para compilar nosso contrato inteligente, devemos primeiro verificar nossa versão do compilador de solidez. Você pode verificar isso executando o comando:

truffle version

A versão padrão é a Solidity v0.5.16. Como nosso token é escrito usando a versão solidity 0.6.2, se executarmos o comando para compilar nossos contratos, obteremos um erro do compilador. Para especificar qual versão do compilador de solidez será usada, acesse o arquivo truffle-config. s e são definidos para a versão desejada do compilador como vistos abaixo:

1// Configure your compilers
2compilers: {
3 solc: {
4 version: "^0.8.0", // Fetch exact version from solc-bin (default: truffle's version)
5 // docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
6 // settings: { // See the solidity docs for advice about optimization and evmVersion
7 // optimizer: {
8 // enabled: false,
9 // runs: 200
10 // },
11 // evmVersion: "byzantium"
12 // }
13 }
14}
Exibir tudo

Agora podemos compilar nosso contrato inteligente executando o seguinte comando:

truffle compile

Instalar Token ERC20

Depois de compilado, podemos publicar nosso token.

Na pasta de migrations, crie um arquivo chamado 2_deploy_Tokens.js. Este arquivo é onde implantaremos nosso token ERC20 e nosso contrato inteligente FarmToken. O código abaixo é usado para publicar nosso contrato MyToken.sol:

1const MyToken = artifacts.require("MyToken")
2
3module.exports = async function (deployer, network, accounts) {
4 // Deploy MyToken
5 await deployer.deploy(MyToken)
6 const myToken = await MyToken.deployed()
7}

Abra o Ganache e selecione a opção "Quickstart" para iniciar uma blockchain local de Ethereum. Para publicar nosso contrato, execute:

truffle migrate

O endereço usado para implantar nossos contratos é o primeiro da lista de endereços que o Ganache nos mostra. Para verificar isso, podemos abrir o aplicativo de trabalho Ganache e podemos verificar se o saldo de ether para a primeira conta foi reduzido devido ao custo de ether para a implantação dos nossos contratos inteligentes:

Aplicativo de desktop Ganache Ui(opens in a new tab)Aplicativo de desktop Ganache Ui

Para verificar que 1 milhão de tokens MyToken foram enviados para o endereço de deploy, podemos utilizar o Truffle Console para interagir com o nosso contrato inteligente que foi publicado.

Truffle Console é um console básico interativo conectando-se a qualquer cliente Ethereum.(opens in a new tab)

Para interagir com nosso contrato inteligente, execute o seguinte comando:

truffle console

Agora podemos escrever os seguintes comandos no terminal:

  • Obter o contrato inteligente: meuToken = await MyToken.deployed()

  • Obter o array de contas de Ganache: contas = aguardar web3.eth.getAccounts()

  • Obter o saldo para a primeira conta: balance = await myToken.balanceOf(contas[0])

  • Formate o saldo de 18 decimals: web3.utils.fromWei(balance.toString())

Executando os comandos acima, vamos ver que o primeiro endereço tem na verdade 1 milhão de MyTokens:

Primeiro endereço tem 1000000 MyTokens(opens in a new tab)

Primeiro endereço tem 1000000 MyTokens

Criando FarmToken Smart Contract

O contrato inteligente FarmToken terá 3 funções:

  • balance(): Obter o balanço do MyToken no contrato inteligente FarmToken.

  • deposit(uint256 _amount): Transfira MyToken em nome do usuário para o contrato inteligente FarmToken e então importe FarmToken para o usuário.

  • withdraw(uint256 _amount): Queimar FarmTokens do usuário e transferir MyTokens para o endereço do usuário.

Vamos dar uma olhada no construtor do FarmToken:

1pragma solidity ^0.6.2;
2
3import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
4import "@openzeppelin/contracts/utils/Address.sol";
5import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
6import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
7
8contract FarmToken is ERC20 {
9 using Address for address;
10 using SafeMath for uint256; // As of Solidity v0.8.0, mathematical operations can be done safely without the need for SafeMath
11 using SafeERC20 for IERC20;
12
13 IERC20 public token;
14
15 constructor(address _token)
16 public
17 ERC20("FarmToken", "FRM")
18 {
19 token = IERC20(_token);
20 }
Exibir tudo
Copiar
  • Linhas 3-6: Estamos importando os seguintes contratos do openzeppelin: IERC20.sol, Address.sol, SafeERC20.sol e ERC20.sol.

  • Linha 8: O FarmToken vai herdar do contrato ERC20.

  • Linhas 14-19: O construtor FarmToken receberá como parâmetro o endereço do contrato MyToken e atribuiremos seu contrato à nossa variável pública chamada token.

Vamos implementar a função balance(). Ele não receberá parâmetros e retornará o saldo do MyToken neste contrato inteligente. Ela está implementada como mostrado abaixo:

1function balance() public view returns (uint256) {
2 return token.balanceOf(address(this));
3}
Copiar

Para a função deposit(uint256 _amount), ele receberá como parâmetro a quantia que o usuário deseja depositar e irá fazer a cunhagem e transferir FarmTokens para o usuário:

1function deposit(uint256 _amount) public {
2 // Amount must be greater than zero
3 require(_amount > 0, "amount cannot be 0");
4
5 // Transfer MyToken to smart contract
6 token.safeTransferFrom(msg.sender, address(this), _amount);
7
8 // Mint FarmToken to msg sender
9 _mint(msg.sender, _amount);
10}
Exibir tudo
Copiar

Para a função withdraw(uint256 _amount), nós vamos receber como parâmetro a quantidade de FarmTokens que o usuário deseja queimar e então vamos transferir a mesma quantidade de MyTokens de volta para o usuário:

1function withdraw(uint256 _amount) public {
2 // Burn FarmTokens from msg sender
3 _burn(msg.sender, _amount);
4
5 // Transfer MyTokens from this smart contract to msg sender
6 token.safeTransfer(msg.sender, _amount);
7}
Copiar

Como implantar um contrato inteligente. Para fazer isso, vamos voltar para o arquivo 2_deploy_Tokens.js e adicionar o novo contrato a ser implantado:

1const MyToken = artifacts.require("MyToken")
2const FarmToken = artifacts.require("FarmToken")
3
4module.exports = async function (deployer, network, accounts) {
5 // Deploy MyToken
6 await deployer.deploy(MyToken)
7 const myToken = await MyToken.deployed()
8
9 // Deploy Farm Token
10 await deployer.deploy(FarmToken, myToken.address)
11 const farmToken = await FarmToken.deployed()
12}
Exibir tudo

Note que ao implantar o FarmToken, passamos como parâmetro o endereço do contrato MyToken implantado.

Agora, rode truffle compilar e truffle migrar para implantar nossos contratos.

Vamos testar o nosso contrato inteligente. Em vez de usar o truffle console para interagir com o nosso contrato inteligente, criaremos um script para automatizar esse processo. Crie uma pasta chamada scripts e adicione o seguinte arquivo getMyTokenBalance.js. Ele irá verificar o saldo dos MyTokens no contrato inteligente do Farmtoken:

1const MyToken = artifacts.require("MyToken")
2const FarmToken = artifacts.require("FarmToken")
3
4module.exports = async function (callback) {
5 myToken = await MyToken.deployed()
6 farmToken = await FarmToken.deployed()
7 balance = await myToken.balanceOf(farmToken.address)
8 console.log(web3.utils.fromWei(balance.toString()))
9 callback()
10}
Exibir tudo

Para executar esse script, execute o seguinte comando na linha de comando:

truffle exec .\scripts\getMyTokenBalance.js

Vamos obter o resultado esperado que é 0. Se você receber um erro sobre o FarmToken ainda não foi implantado, a rede truffle não recebeu a versão mais recente do seu código de contratos. Apenas feche o ganache, reinicie o programa rapidamente e certifique-se de executar a migração de um truffle.

Agora, vamos fazer o staking do MyToken para o contrato inteligente. Desde a função deposit(uint256 _amount) chama a função safeTransferFrom do ERC20, primeiro o usuário deve aprovar o contrato inteligente para transferir MyToken em nome do usuário. Então, no script abaixo, primeiro aprovaremos esta etapa e então chamaremos a função:

1const MyToken = artifacts.require("MyToken")
2const FarmToken = artifacts.require("FarmToken")
3
4module.exports = async function (callback) {
5 const accounts = await new web3.eth.getAccounts()
6 const myToken = await MyToken.deployed()
7 const farmToken = await FarmToken.deployed()
8
9 // Returns the remaining number of tokens that spender will be allowed to spend on behalf of owner through transferFrom.
10 // This is zero by default.
11 const allowanceBefore = await myToken.allowance(
12 accounts[0],
13 farmToken.address
14 )
15 console.log(
16 "Amount of MyToken FarmToken is allowed to transfer on our behalf Before: " +
17 allowanceBefore.toString()
18 )
19
20 // In order to allow the Smart Contract to transfer to MyToken (ERC-20) on the accounts[0] behalf,
21 // we must explicitly allow it.
22 // We allow farmToken to transfer x amount of MyToken on our behalf
23 await myToken.approve(farmToken.address, web3.utils.toWei("100", "ether"))
24
25 // Validate that the farmToken can now move x amount of MyToken on our behalf
26 const allowanceAfter = await myToken.allowance(accounts[0], farmToken.address)
27 console.log(
28 "Amount of MyToken FarmToken is allowed to transfer on our behalf After: " +
29 allowanceAfter.toString()
30 )
31
32 // Verify accounts[0] and farmToken balance of MyToken before and after the transfer
33 balanceMyTokenBeforeAccounts0 = await myToken.balanceOf(accounts[0])
34 balanceMyTokenBeforeFarmToken = await myToken.balanceOf(farmToken.address)
35 console.log("*** My Token ***")
36 console.log(
37 "Balance MyToken Before accounts[0] " +
38 web3.utils.fromWei(balanceMyTokenBeforeAccounts0.toString())
39 )
40 console.log(
41 "Balance MyToken Before TokenFarm " +
42 web3.utils.fromWei(balanceMyTokenBeforeFarmToken.toString())
43 )
44
45 console.log("*** Farm Token ***")
46 balanceFarmTokenBeforeAccounts0 = await farmToken.balanceOf(accounts[0])
47 balanceFarmTokenBeforeFarmToken = await farmToken.balanceOf(farmToken.address)
48 console.log(
49 "Balance FarmToken Before accounts[0] " +
50 web3.utils.fromWei(balanceFarmTokenBeforeAccounts0.toString())
51 )
52 console.log(
53 "Balance FarmToken Before TokenFarm " +
54 web3.utils.fromWei(balanceFarmTokenBeforeFarmToken.toString())
55 )
56 // Call Deposit function from FarmToken
57 console.log("Call Deposit Function")
58 await farmToken.deposit(web3.utils.toWei("100", "ether"))
59 console.log("*** My Token ***")
60 balanceMyTokenAfterAccounts0 = await myToken.balanceOf(accounts[0])
61 balanceMyTokenAfterFarmToken = await myToken.balanceOf(farmToken.address)
62 console.log(
63 "Balance MyToken After accounts[0] " +
64 web3.utils.fromWei(balanceMyTokenAfterAccounts0.toString())
65 )
66 console.log(
67 "Balance MyToken After TokenFarm " +
68 web3.utils.fromWei(balanceMyTokenAfterFarmToken.toString())
69 )
70
71 console.log("*** Farm Token ***")
72 balanceFarmTokenAfterAccounts0 = await farmToken.balanceOf(accounts[0])
73 balanceFarmTokenAfterFarmToken = await farmToken.balanceOf(farmToken.address)
74 console.log(
75 "Balance FarmToken After accounts[0] " +
76 web3.utils.fromWei(balanceFarmTokenAfterAccounts0.toString())
77 )
78 console.log(
79 "Balance FarmToken After TokenFarm " +
80 web3.utils.fromWei(balanceFarmTokenAfterFarmToken.toString())
81 )
82
83 // End function
84 callback()
85}
Exibir tudo

Para rodar esse script: truffle exec .\scripts\transferMyTokenToFarmToken.js. Você deve ver no seu console:

output do transferMyTokenToFarmToken.js(opens in a new tab)

output do transferMyTokenToFarmToken.js

Como podemos ver, depositamos MyTokens com sucesso no contrato inteligente já que a primeira conta agora tem FarmTokens.

Para retirar:

1const MyToken = artifacts.require("MyToken")
2const FarmToken = artifacts.require("FarmToken")
3
4module.exports = async function (callback) {
5 const accounts = await new web3.eth.getAccounts()
6 const myToken = await MyToken.deployed()
7 const farmToken = await FarmToken.deployed()
8
9 // Verify accounts[0] and farmToken balance of MyToken before and after the transfer
10 balanceMyTokenBeforeAccounts0 = await myToken.balanceOf(accounts[0])
11 balanceMyTokenBeforeFarmToken = await myToken.balanceOf(farmToken.address)
12 console.log("*** My Token ***")
13 console.log(
14 "Balance MyToken Before accounts[0] " +
15 web3.utils.fromWei(balanceMyTokenBeforeAccounts0.toString())
16 )
17 console.log(
18 "Balance MyToken Before TokenFarm " +
19 web3.utils.fromWei(balanceMyTokenBeforeFarmToken.toString())
20 )
21
22 console.log("*** Farm Token ***")
23 balanceFarmTokenBeforeAccounts0 = await farmToken.balanceOf(accounts[0])
24 balanceFarmTokenBeforeFarmToken = await farmToken.balanceOf(farmToken.address)
25 console.log(
26 "Balance FarmToken Before accounts[0] " +
27 web3.utils.fromWei(balanceFarmTokenBeforeAccounts0.toString())
28 )
29 console.log(
30 "Balance FarmToken Before TokenFarm " +
31 web3.utils.fromWei(balanceFarmTokenBeforeFarmToken.toString())
32 )
33
34 // Call Deposit function from FarmToken
35 console.log("Call Withdraw Function")
36 await farmToken.withdraw(web3.utils.toWei("100", "ether"))
37
38 console.log("*** My Token ***")
39 balanceMyTokenAfterAccounts0 = await myToken.balanceOf(accounts[0])
40 balanceMyTokenAfterFarmToken = await myToken.balanceOf(farmToken.address)
41 console.log(
42 "Balance MyToken After accounts[0] " +
43 web3.utils.fromWei(balanceMyTokenAfterAccounts0.toString())
44 )
45 console.log(
46 "Balance MyToken After TokenFarm " +
47 web3.utils.fromWei(balanceMyTokenAfterFarmToken.toString())
48 )
49
50 console.log("*** Farm Token ***")
51 balanceFarmTokenAfterAccounts0 = await farmToken.balanceOf(accounts[0])
52 balanceFarmTokenAfterFarmToken = await farmToken.balanceOf(farmToken.address)
53 console.log(
54 "Balance FarmToken After accounts[0] " +
55 web3.utils.fromWei(balanceFarmTokenAfterAccounts0.toString())
56 )
57 console.log(
58 "Balance FarmToken After TokenFarm " +
59 web3.utils.fromWei(balanceFarmTokenAfterFarmToken.toString())
60 )
61
62 // End function
63 callback()
64}
Exibir tudo

Para rodar esse script: truffle exec .\scripts\transferMyTokenToFarmToken.js. Como podemos ver no output abaixo, nós conseguimos de volta os MyTokens com sucesso e acabamos com os FarmTokens:

output do withdrawMyTokenFromTokenFarm.js(opens in a new tab)

output do withdrawMyTokenFromTokenFarm.js

Referências

Contratos - OpenZeppelin Docs(opens in a new tab)

Ferramentas Suplentes para Contratos Inteligentes Common Suite(opens in a new tab)

Ganache | Truffle Suite(opens in a new tab)

O que é DeFi? Um guia para iniciantes (atualizado em 2021) (99bitcoins.com)(opens in a new tab)

DeFi - A classificação da finança descentraliza no DeFi Llama(opens in a new tab)

Última edição: @teachertialorena(opens in a new tab), 19 de janeiro de 2024

Este tutorial foi útil?