Criar e promover um Aplicativo DeFi
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.
(opens in a new tab)Aplicativo de desktop Ganache UI
Para criar o projeto, execute os seguintes passos
mkdir your-project-namecd your-project-nametruffle 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çãotest
: Pasta para testar nossos contratos inteligentestruffle-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;23import "@openzeppelin/contracts/token/ERC20/ERC20.sol";45contract MyToken is ERC20 {6 constructor() public ERC20("MyToken", "MTKN"){7 _mint(msg.sender, 1000000000000000000000000);8 }9}Exibir tudoCopiar
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;45constructor (string memory name_, string memory symbol_) public {6 _name = name_;7 _symbol = symbol_;8 _decimals = 18;9}Exibir tudoCopiar
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 compilers2compilers: {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 evmVersion7 // optimizer: {8 // enabled: false,9 // runs: 20010 // },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")23module.exports = async function (deployer, network, accounts) {4 // Deploy MyToken5 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:
(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.
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
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;23import "@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";78contract 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 SafeMath11 using SafeERC20 for IERC20;1213 IERC20 public token;1415 constructor(address _token)16 public17 ERC20("FarmToken", "FRM")18 {19 token = IERC20(_token);20 }Exibir tudoCopiar
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 zero3 require(_amount > 0, "amount cannot be 0");45 // Transfer MyToken to smart contract6 token.safeTransferFrom(msg.sender, address(this), _amount);78 // Mint FarmToken to msg sender9 _mint(msg.sender, _amount);10}Exibir tudoCopiar
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 sender3 _burn(msg.sender, _amount);45 // Transfer MyTokens from this smart contract to msg sender6 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")34module.exports = async function (deployer, network, accounts) {5 // Deploy MyToken6 await deployer.deploy(MyToken)7 const myToken = await MyToken.deployed()89 // Deploy Farm Token10 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")34module.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")34module.exports = async function (callback) {5 const accounts = await new web3.eth.getAccounts()6 const myToken = await MyToken.deployed()7 const farmToken = await FarmToken.deployed()89 // 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.address14 )15 console.log(16 "Amount of MyToken FarmToken is allowed to transfer on our behalf Before: " +17 allowanceBefore.toString()18 )1920 // 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 behalf23 await myToken.approve(farmToken.address, web3.utils.toWei("100", "ether"))2425 // Validate that the farmToken can now move x amount of MyToken on our behalf26 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 )3132 // Verify accounts[0] and farmToken balance of MyToken before and after the transfer33 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 )4445 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 FarmToken57 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 )7071 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 )8283 // End function84 callback()85}Exibir tudo
Para rodar esse script: truffle exec .\scripts\transferMyTokenToFarmToken.js
. Você deve ver no seu console:
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")34module.exports = async function (callback) {5 const accounts = await new web3.eth.getAccounts()6 const myToken = await MyToken.deployed()7 const farmToken = await FarmToken.deployed()89 // Verify accounts[0] and farmToken balance of MyToken before and after the transfer10 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 )2122 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 )3334 // Call Deposit function from FarmToken35 console.log("Call Withdraw Function")36 await farmToken.withdraw(web3.utils.toWei("100", "ether"))3738 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 )4950 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 )6162 // End function63 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
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