Ir al contenido principal

Crear e implementar una aplicación DeFi

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

En este tutorial construiremos una aplicación DeFi con Solidity donde los usuarios pueden depositar un token ERC20 en el contrato inteligente y les acuñará y transferirá Farm Tokens. Los usuarios pueden retirar más tarde sus tokens ERC20 quemando su Farm Token en un contrato inteligente y los tokens ERC20 se transferirán de nuevo a ellos.

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 construir e implementar contratos inteligentes en la cadena de bloques. Ganache nos permite crear una cadena de bloques ethereum local para probar contratos inteligentes. Simula las características de la red real y las primeras 10 cuentas se financian 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 una aplicación de escritorio y una herramienta de línea de comandos. Para este artículo usaremos la aplicación de escritorio UI.

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

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 y despliegue de nuestros contratos inteligentes. La estructura del proyecto creado es la siguiente:

  • contracts: Carpeta para los contratos inteligentes (escritos en Solidity)

  • migraciones: Carpeta para los scripts de despliegue

  • 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 en el contrato inteligente. Para crear nuestro fungible token, primero necesitamos instalar la librería OpenZeppelin. Esta librería contiene las implementaciones de estándares como ERC20 y ERC721. Para instalarlo, ejecute el comando:

npm install @openzeppelin/contracts

Utilizando la librería OpenZeppelin podemos crear nuestro token ERC20 escribiendo a contracts/MyToken.sol con el siguiente código de solidez:

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:

  • Linea 3: Importamos el contrato ERC-20.sol desde openzeppelin que contiene la implementación para este token estándar.

  • Linea 5: Heredamos desde el contrato ERC-20.sol.

  • Linea 6: Estamos llamando al contructor ERC20.sol y pasando los parametros del nombre y simbolo como "MyToken" y "MTKN" respectivamente.

  • Linea 7: Estamos minteando y transfiriendo 1 millon de tokens para la cuenta que esta desplegando el contrato inteligente (estamos usando los 18 decimales por defecto del token ERC20, eso significa que si queremos mintear 1 token, lo podes representar como 1000000000000000000, 1 con 18 ceros).

Debajo podemos ver la implementacion del constructor del ERC20.sol, donde _decimals campo esta 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 solidity. Puedes verificarla ejecutando el comando:

truffle version

La version por defecto es Solidity v0.5.16. Ya que 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 compilación. En orden 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

Desplegar Token ERC20

Despues de compilar, ahora podemos desplegar nuestro token.

En la carpeta de migrations, crear un archivo llamado 2_deploy_Tokens.js. Este archivo es donde vamos a desplegar tanto nuestro Token ERC20 y nuestro contrato inteligente FarmToken. El siguiente código se utiliza para desplegar 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}

Abre Ganache y selecciona la opción "Quickstart" (comienzo rápido) para comenzar una blockchain local de Ethereum. Para desplegar nuestro contrato, ejecutar:

truffle migrate

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

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

Para verificar que 1 millón de tokens de MyToken han sido enviados a la dirección del desplegador, 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, ejecuta el siguiente comando:

truffle console

Ahora podemos escribir los siguientes comandos en la terminal:

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

  • Obtener la formación de cuentas de Ganache: accounts = await web3.eth.getAccounts()

  • Obtener el balance 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 contrato inteligente FarmToken

El contrato inteligente FarmToken tendrá 3 funciones:

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

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

  • retiro (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 asignaremos 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 como se muestra a continuación:

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

Para la función deposito (uint256 _amount), recibirá como parámetro la cantidad que el usuario quiere depositar y minteara 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 de retiro (uint256 _amount), recibiremos como parámetro la cantidad de FarmTokens que el usuario desea grabar 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 desplegaremos 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 desplegar FarmToken, pasamos como parámetro la dirección del contrato desplegado MyToken.

Ahora, ejecuta truffle compile y truffle migrate para desplegar 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. Crea una carpeta llamada scripts y añade 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:

truffle exec .\scripts\getMyTokenBalance.js

Obtendremos el resultado esperado, que es 0. Si recibes un error sobre el FarmToken indicando que aún no está desplegando, se debe a que la red de Truffle no ha recibido la última versión del código de tu contrato. Cierra Ganache, vuelve a iniciarlo y asegúrate de ejecutar truffle migrate.

Ahora, vamos a depositar 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 que este pueda transferir MyToken en nombre del usuario. Así que, en el siguiente guión, primero ejecutaremos este paso de aprobación 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ías ver en la consola:

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

salida de transferMyTokenToFarmToken.js

Como podemos ver, la primera cuenta del contrato inteligente ya tiene FarmTokens lo que indica que hemos depositado con éxito MyTokens.

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 withdrawMyTokenFromTokenFarm.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? Una guia 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)

¿Le ha resultado útil este tutorial?