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), Invalid DateTime

Este tutorial foi útil?