Ir al contenido principal

Crear e implementar una aplicación DeFi

solidezdefiweb3.jstruffleganachecontratos inteligentes
Intermedio
strykerin
github.com(opens in a new tab)
31 de diciembre de 2020
11 minuto leído minute read

En este tutorial crearemos una aplicación DeFi con Solidity donde los usuarios pueden depositar un token ERC20 en el contrato inteligente y este minteará y transferirá Farm Tokens. Los usuarios pueden retirar más tarde sus tokens ERC20 quemando su Farm Token en un contrato inteligente, y se les devolverán los tokens ERC20.

Instalar Truffle y Ganache

Si es la primera vez que escribe un contrato inteligente, deberá configurar su entorno. Vamos a utilizar dos herramientas: Truffle(opens in a new tab) y Ganache(opens in a new tab).

Truffle es un entorno de desarrollo y marco de pruebas para desarrollar contratos inteligentes para Ethereum. Con Truffle es fácil crear e implementar contratos inteligentes en la cadena de bloques. Ganache nos permite crear una cadena de bloques de Ethereum local para probar contratos inteligentes. Simula las características de la red real y las primeras 10 cuentas cuentan con 100 ether de prueba, lo que hace que la implementación y las pruebas de contratos inteligentes sean gratuitas y fáciles. Ganache está disponible como aplicación de escritorio y herramienta de línea de comandos. Para este artículo usaremos la aplicación de escritorio de UI.

Aplicación de escritorio de UI de Ganache(opens in a new tab)Aplicación de escritorio de UI de Ganache

Para crear el proyecto, ejecute los siguientes comandos:

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

Esto creará un proyecto en blanco para el desarrollo e implementación de nuestros contratos inteligentes. La estructura de proyecto creada es la siguiente:

  • contracts: Carpeta para los contratos inteligentes de Solidity

  • migrations: Carpeta para los scripts de implementación

  • test: Carpeta para probar nuestros contratos inteligentes

  • truffle-config.js: Archivo de configuración Truffle

Crear el token ERC20

Primero necesitamos crear nuestro token ERC20 que utilizaremos para apostar, o hacer staking, en el contrato inteligente. Para crear nuestro token fungible, primero necesitamos instalar la biblioteca OpenZeppelin. Esta biblioteca contiene las implementaciones de estándares como ERC20 y ERC721. Para instalarla, ejecute el comando:

npm install @openzeppelin/contracts

Utilizando la biblioteca OpenZeppelin podemos crear nuestro token ERC20 escribiendo a contracts/MyToken.sol con el siguiente código de 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}
Mostrar todo
Copiar

En el código de arriba en:

  • Línea 3: Importamos el contrato ERC-20.sol desde openzeppelin que contiene la implementación para este estándar de tokens.

  • Línea 5: Heredamos desde el contrato ERC-20.sol.

  • Línea 6: Estamos llamando al contructor ERC20.sol y pasando los parámetros de nombre y símbolo como "MyToken" y "MTKN", respectivamente.

  • Línea 7: Estamos minteando y transfiriendo 1 millon de tokens para la cuenta que está implementando el contrato inteligente (estamos usando los 18 decimales por defecto del token ERC20; eso significa que, si queremos mintear 1 token, lo representaremos como 1000000000000000000, 1 con 18 ceros).

Debajo podemos ver la implementacion del constructor ERC20.sol, donde el campo _decimals está establecido en 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}
Mostrar todo
Copiar

Compilar el token ERC20

Para compilar nuestro contrato inteligente, primer debemos verificar la versión de nuestro compilador de Solidity. Puede verificarla ejecutando el comando:

truffle version

La versión por defecto es Solidity v0.5.16. Como nuestro token está escrito usando la versión de Solidity 0.6.2, si corremos el comando para compilar nuestros contratos, obtendremos un error de compilador. Para especificar qué versión del compilador de Solidity usar, vaya al archivo truffle-config.js y establezca la versión deseada del compilador como se ve a continuación:

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}
Mostrar todo

Ahora podemos compilar nuestro contrato inteligente ejecutando el siguiente comando:

truffle compile

Implementar el token ERC20

Despues de compilar, ahora podemos implementar nuestro token.

En la carpeta migrations, cree un archivo llamado 2_deploy_Tokens.js. Este archivo es donde vamos a implementar tanto nuestro token ERC20 como nuestro contrato inteligente FarmToken. El siguiente código se utiliza para implementar nuestro 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 Ganache y seleccione la opción "Quickstart" para comenzar una cadena de bloques local de Ethereum. Para implementar nuestro contrato, ejecute:

truffle migrate

La dirección utilizada para implementar nuestros contratos es la primera de la lista de direcciones que Ganache nos muestra. Para verificarlo, podemos abrir la aplicacion de escritorio de Ganache y verificar que el saldo de ether de nuestra primera cuenta haya sido reducido debido al costo de ether para implementar nuestros contratos inteligentes:

Aplicación de escritorio de Ganache(opens in a new tab)Aplicación de escritorio de Ganache

Para verificar que se hayan enviado 1 millón de tokens de MyToken a la dirección del implementador, podemos usar la consola de Truffle para interactuar con nuestro contrato inteligente implementado.

Truffle Console es una consola básica e interactiva que se conecta con cualquier cliente de Ethereum.(opens in a new tab)

Para poder interactuar con nuestro contrato inteligente, ejecute el siguiente comando:

truffle console

Ahora podemos escribir los siguientes comandos en el terminal:

  • Obtener el contrato inteligente: myToken = await MyToken.deployed()

  • Obtener el array de cuentas de Ganache: accounts = await web3.eth.getAccounts()

  • Obtener el saldo de la primera cuenta: balance = await myToken.balanceOf(accounts[0])

  • Formatear el saldo a partir de 18 decimales: web3.utils.fromWei(balance.toString())

Al ejecutar los comandos de arriba, veremos que la primera dirección tiene de hecho 1 millón de MyTokens:

La primera dirección tiene 1000000 MyTokens(opens in a new tab)

La primera dirección tiene 1000000 MyTokens

Crear un contrato inteligente FarmToken

El contrato inteligente FarmToken tendrá 3 funciones:

  • balance(): Obtener el saldo de MyToken en el contrato inteligente FarmToken.

  • deposit(uint256 _amount): Transferir MyToken en nombre del usuario al contrato inteligente FarmToken y luego mintear y transferir FarmToken al usuario.

  • withdraw(uint256 _amount): Quemar FarmTokens del usuario y transferir MyTokens a la dirección del usuario.

Veamos el constructor de 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 }
Mostrar todo
Copiar
  • Líneas 3-6: Importamos los siguientes contratos de openzeppelin: IERC20.sol, Address.sol, SafeERC20.sol y ERC20.sol.

  • Línea 8: El FarmToken heredará del contrato ERC20.

  • Líneas 14-19: El constructor de FarmToken recibirá como parámetro la dirección del contrato MyToken y asignará su contrato a nuestra variable pública llamada token.

Vamos a implementar la función balance(). No recibirá ningún parámetro y devolverá el saldo de MyToken en este contrato inteligente. Se implementa así:

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

Para la función deposite(uint256 _amount), recibirá como parámetro la cantidad que el usuario quiere depositar y minteará y transferirá FarmTokens al usuario:

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}
Mostrar todo
Copiar

Para la función withdraw(uint256 _amount), recibiremos como parámetro la cantidad de FarmTokens que el usuario desea quemar y luego transferir la misma cantidad de MyTokens de vuelta al usuario:

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

Ahora implementaremos nuestro contrato inteligente. Para hacerlo, regresaremos al archivo 2_deploy_Tokens.js y agregaremos el nuevo contrato que se va a implementar:

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}
Mostrar todo

Tenga en cuenta que, al implementar FarmToken, pasamos como parámetro la dirección del contrato implementado MyToken.

Ahora, ejecute truffle compile y truffle migrate para implementar nuestros contratos.

Probemos nuestro contrato inteligente. En lugar de usar la consola de truffle para interactuar con nuestro contrato inteligente, crearemos un script para automatizar este proceso. Cree una carpeta llamada scripts y añada el siguiente archivo getMyTokenBalance.js. Comprobará el saldo de MyTokens en el contrato inteligente 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}
Mostrar todo

Para ejecutar este script, ejecute el siguiente comando de CLI:

truffle exec .\scripts\getMyTokenBalance.js

Obtendremos el resultado esperado, que es 0. Si recibe un error que indica que el FarmToken aún no se implementó, se debe a que la red de Truffle no ha recibido la última versión del código de su contrato. Cierre Ganache, vuelva a iniciarlo y asegúrese de ejecutar truffle migrate.

Ahora, vamos a apostar MyToken en el contrato inteligente. Puesto que la función deposit(uint256 _amount) llama a la función safeTransferFrom desde el ERC20, el usuario debe aprobar primero el contrato inteligente para transferir MyToken en nombre del usuario. Así que, en el siguiente script, primero aprobaremos este paso y luego llamaremos a la función:

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}
Mostrar todo

Para ejecutar este script: truffle exec .\scripts\transferMyTokenToFarmToken.js. Debería ver en la consola:

salida de transferMyTokenToFarmToken.js(opens in a new tab)

salida de transferMyTokenToFarmToken.js

Como podemos ver, hemos depositado exitosamente MyTokens en el contrato inteligente, ya que la primera cuenta ahora tiene FarmTokens.

Para poder 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}
Mostrar todo

Para ejecutar este script: truffle exec .\scripts\withdrawMyTokenFromTokenFarm.js. Como podemos ver a continuación, hemos recuperado con éxito los MyTokens y quemado los FarmTokens:

salida de DropMyTokenFromTokenFarm.js(opens in a new tab)

salida de DropMyTokenFromTokenFarm.js

Referencias

Contratos: documentos OpenZeppelin(opens in a new tab)

Sweet Tools for Smart Contracts | Truffle Suite(opens in a new tab)

Ganache | Truffle Suite(opens in a new tab)

¿Qué es DeFi? Guía para principiantes (actualizado 2021) (99bitcoins.com)(opens in a new tab)

DeFi - La tabla de clasificación de finanzas descentralizadas en DeFi Llama(opens in a new tab)

Última edición: @nhsz(opens in a new tab), 15 de agosto de 2023

¿Le ha resultado útil este tutorial?