Cómo crear un token ERC-20 en la red Ethereum

Contenidos ocultar

¿Qué es un token ERC-20?

La cadena de bloques Ethereum le permite crear su propia criptomoneda, o token, que se puede comprar con Ether, la criptomoneda nativa de la cadena de bloques Ethereum. ERC-20 es simplemente un estándar que especifica cómo se comportan estos tokens, para que sean compatibles con otras plataformas como los intercambios de criptomonedas.

Ethereum es una cadena de bloques como Bitcoin. Al igual que Bitcoin, Ethereum realiza un seguimiento de los saldos de las cuentas de las personas que poseen Ether, la criptomoneda nativa de Ethereum. A diferencia de Bitcoin, Ethereum también es una plataforma que le permite crear su propio token sin crear una nueva cadena de bloques.

Puede crear un token Ethereum con un contrato inteligente. ERC-20 es un estándar que especifica cómo debería funcionar este contrato inteligente de token. Usemos un ejemplo para comprender cómo funciona un contrato inteligente de token ERC-20. Digamos que queremos crear un token llamado «Token» con el símbolo «TKN» y que habrá 10.000.000 de estos tokens en existencia.

Primero, el contrato inteligente del token realiza un seguimiento de algunos atributos básicos del token. Por ejemplo, registra el nombre «Token», el símbolo que ve en un intercambio de criptomonedas y cuántos tokens en total existen. También realiza un seguimiento de quién posee «Token» y cuánto.

Los tokens ERC-20 se pueden transferir de una cuenta a otra como pago, como cualquier otra criptomoneda. También se pueden comprar en una venta colectiva, como una ICO, que examinaremos en la siguiente sección. También se pueden comprar y vender en un intercambio de criptomonedas.

¿Cómo funciona una venta colectiva (ICO)?

Los tokens ERC-20 se pueden distribuir de varias formas. Un método popular es realizar una venta colectiva o una oferta inicial de monedas (ICO). Las ventas colectivas son una forma de que una empresa obtenga capital para su negocio mediante la creación de su propio token ERC-20 que los inversores pueden comprar con Ether.

Puede ver este video a continuación para obtener más información sobre cómo funcionan las ventas colectivas, además de seguir este artículo.

Siempre que se lleva a cabo una venta colectiva, la compañía obtiene capital líquido en forma de Ether que fue pagado por los inversores, además de retener una cantidad reservada de los tokens ERC-20 que se vendieron en la venta colectiva.

Para participar en una venta colectiva, un inversor debe conectarse a Etherum Blockchain con una cuenta. Esta cuenta tiene una dirección de billetera que puede almacenar Ether, así como los tokens ERC-20 que se compran en la venta colectiva.

El inversor debe visitar un sitio web de venta colectiva que habla de un contrato inteligente. El contrato inteligente rige todas las reglas sobre cómo funciona la venta colectiva.

Cada vez que un inversor compra tokens en el sitio web de venta colectiva, envía Ether desde su billetera al contrato inteligente, y el contrato inteligente distribuye instantáneamente los tokens comprados en su billetera. El contrato inteligente establece el precio del token en la venta colectiva y gobierna cómo se comporta la venta colectiva.

Las ventas colectivas pueden adoptar todo tipo de formas y tamaños. Pueden tener múltiples niveles o fases, como la fase de bonificación Pre ICO, ICO y ICO. Cada uno de estos niveles puede ocurrir en diferentes momentos y puede comportarse de manera diferente. También pueden tener listas blancas para restringir qué inversores pueden comprar tokens.

También pueden tener una cantidad reservada de tokens que no se venden en la venta colectiva. Estas reservas generalmente se reservan para miembros específicos de cada empresa, como fundadores y asesores. Estas reservas pueden ser una cantidad fija de tokens o un porcentaje.

Siempre que finaliza una venta colectiva, un administrador puede finalizarla. Siempre que esto suceda, todos los tokens reservados se distribuirán a las cuentas correspondientes y la venta colectiva finalizará oficialmente.

Cómo funcionan los tokens ERC-20

Como expliqué anteriormente, los tokens ERC-20 se crean con contratos inteligentes de Ethereum. Entonces, ¿qué es un contrato inteligente?

Ethereum permite a los desarrolladores escribir aplicaciones que se ejecutan en la cadena de bloques con contratos inteligentes, que encapsulan toda la lógica empresarial de estas aplicaciones. Nos permiten leer y escribir datos en la cadena de bloques, así como ejecutar código. 

Los contactos inteligentes están escritos en un lenguaje de programación llamado Solidity, que se parece mucho a Javascript. Es un lenguaje de programación en toda regla que nos permitirá hacer muchos de los mismos tipos de cosas que Javascript es capaz de hacer, pero se comporta un poco diferente debido a su caso de uso, como veremos en este tutorial.

En el caso de un token ERC-20, el contrato inteligente rige todo el comportamiento sobre cómo funciona el token y realiza un seguimiento de la propiedad del token y los saldos de las cuentas.

ERC-20 es una especificación API sobre cómo se deben construir los tokens Ethereum. Es un estándar adoptado por la comunidad que permite que los tokens sean compatibles en una variedad de casos de uso. Queremos construir un token que cumpla con este estándar para que pueda ser ampliamente aceptado. Si no tuviéramos un estándar como este, podríamos tener infinitas formas de crear tokens, ¡y es posible que no sean compatibles entre sí!

El uso del estándar ERC-20 garantiza que un token sea compatible con los siguientes casos de uso (y más):

  • Transferencias de billetera: envío de tokens de una cuenta a otra
  • Compra y venta en intercambios de criptomonedas
  • Comprar tokens en una venta colectiva (ICO) como demostraremos en este tutorial

La especificación ERC-20 esencialmente dicta la interfaz a la que debe responder el contrato inteligente. Especifica la estructura del contrato inteligente y los tipos de funciones que debe tener el contrato inteligente. También proporciona algunas funciones sugeridas que es bueno tener, pero que en última instancia son opcionales. 

Dicta ciertos eventos que debe tener nuestra ficha, como un transferevento. Vea, los contratos inteligentes pueden emitir eventos a los que los consumidores pueden suscribirse, y con este estándar, podemos suscribirnos a eventos que nos dicen cuándo se venden los tokens.

A continuación se muestra un ejemplo de implementación de la transferfunción especificada por el estándar ERC-20. Es un requisito del contrato inteligente y rige cómo alguien puede enviar un token ERC-20 de su billetera a otro.

contract ERC20Token {     // ...
 
     function transfer(address _to, uint256 _value) public returns (bool success) {         require(balanceOf[msg.sender] >= _value);
 
         balanceOf[msg.sender] -= _value;
         balanceOf[_to] += _value;
 
         Transfer(msg.sender, _to, _value);
 
         return true;
     }
 
     // ... }

Esta función implementa el estándar ERC-20 de las siguientes formas:

  • La función existe.
  • Acepta los argumentos correctos.
  • Falla si el usuario no tiene suficientes tokens para transferir, es decir, un saldo insuficiente.
  • Transfiere el saldo de la cuenta del remitente a la cuenta del receptor.
  • Activa un sellevento.
  • Se devuelve el valor correcto, por ejemplo, true.

Instalación de dependencias

Para construir nuestro token ERC-20 y venta colectiva, primero necesitamos algunas dependencias.

Administrador de paquetes de nodo (NPM)

La primera dependencia que necesitamos es Node Package Manager, o NPM, que viene con Node.js. Puede ver si ya tiene un nodo instalado yendo a su terminal y escribiendo:

$ node -v

Truffle Framework

La siguiente dependencia es Truffle Framework, que nos permite crear aplicaciones descentralizadas en la cadena de bloques Ethereum. Proporciona un conjunto de herramientas que nos permiten escribir contactos inteligentes con el lenguaje de programación Solidity. También nos permite probar nuestros contratos inteligentes e implementarlos en la cadena de bloques. También nos da un lugar para desarrollar nuestra aplicación del lado del cliente.

Puede instalar Truffle con NPM en su línea de comando de esta manera:

$ npm install -g truffle

Ganache

La siguiente dependencia es Ganache, una cadena de bloques en memoria local. Puede instalar Ganache descargándolo del sitio web de Truffle Framework. Nos dará 10 cuentas externas con direcciones en nuestra cadena de bloques Ethereum local. Cada cuenta está precargada con 100 ether falsos.

Metamask

La siguiente dependencia es la extensión Metamask para Google Chrome. Para usar la cadena de bloques, debemos conectarnos a ella. Tendremos que instalar una extensión de navegador especial para usar la cadena de bloques Ethereum. Ahí es donde entra la metamask. Podremos conectarnos a nuestra cadena de bloques Ethereum local con nuestra cuenta personal e interactuar con nuestro contrato inteligente.

Vamos a utilizar la extensión de Chrome Metamask para este tutorial, por lo que también deberá instalar el navegador Google Chrome si aún no lo tiene. Para instalar Metamask, busque el complemento Metamask Chrome en la tienda web de Google Chrome. Una vez que lo haya instalado, asegúrese de que esté marcado en su lista de extensiones. Verá el ícono de zorro en la parte superior derecha de su navegador Chrome cuando esté instalado. 

Resaltado de sintaxis

La dependencia es opcional, pero recomendada. Recomiendo instalar el resaltado de sintaxis para el lenguaje de programación Solidity. La mayoría de los editores de texto e IDE no tienen resaltado de sintaxis para Solidity de fábrica, por lo que tendrá que instalar un paquete para admitir esto. Puedes utilizar Sublime Text descargando el paquete «Ethereum» que proporciona un buen resaltado de sintaxis para Solidity.

Contrato inteligente de token ERC-20

Ahora que están las dependencias instaladas, ¡comencemos a construir nuestro token ERC-20! Aquí está el código completo de solidez del contrato inteligente del token ERC-20:

pragma solidity ^0.4.2;
 
 contract DappToken {
     string  public name = "DApp Token";
     string  public symbol = "DAPP";
     string  public standard = "DApp Token v1.0";
     uint256 public totalSupply;
 
     event Transfer(
         address indexed _from,
         address indexed _to,
         uint256 _value
     );
 
     event Approval(
         address indexed _owner,
         address indexed _spender,
         uint256 _value
     );
 
     mapping(address => uint256) public balanceOf;
     mapping(address => mapping(address => uint256)) public allowance;
 
     function DappToken (uint256 _initialSupply) public {
         balanceOf[msg.sender] = _initialSupply;
         totalSupply = _initialSupply;
     }
 
     function transfer(address _to, uint256 _value) public returns (bool success) {         require(balanceOf[msg.sender] >= _value);
 
         balanceOf[msg.sender] -= _value;
         balanceOf[_to] += _value;
 
         Transfer(msg.sender, _to, _value);
 
         return true;
     }
 
     function approve(address _spender, uint256 _value) public returns (bool success) {
         allowance[msg.sender][_spender] = _value;
 
         Approval(msg.sender, _spender, _value);
 
         return true;
     }
 
     function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {         require(_value <= balanceOf[_from]);         require(_value <= allowance[_from][msg.sender]);
 
         balanceOf[_from] -= _value;
         balanceOf[_to] += _value;
 
         allowance[_from][msg.sender] -= _value;
 
         Transfer(_from, _to, _value);
 
         return true;
     }
 }

Echemos un vistazo a lo que hace este contrato inteligente y cómo implementa el estándar ERC-20:

  • Almacena el nombre del token string public name = "DApp Token".
  • Almacena el símbolo del token para los intercambios de criptomonedas string public symbol = "DAPP".
  • Almacena el suministro total de tokens existentes uint256 public totalSupply.
  • Utiliza un mapeo de Solidez para almacenar el saldo de cada cuenta que posee tokens mapping(address => uint256) public balanceOf.
  • Implementa una transferfunción para permitir a los usuarios enviar tokens a otra cuenta.
  • Implementa una approve función que permite que otra cuenta gaste tokens, como en un intercambio de criptomonedas. Esto actualiza la allowance asignación para ver cuánto puede gastar la cuenta.
  • Implementa un transferFrom que permite que otra cuenta transfiera tokens.

También puede leer las pruebas de este contrato inteligente para descubrir más sobre cómo funciona. Estas pruebas garantizan que este contrato inteligente se comporte de la manera que esperamos. Aquí hay un conjunto de pruebas completo que verifica todo el comportamiento del contrato inteligente:

También puede leer las pruebas de este contrato inteligente para descubrir más sobre cómo funciona. Estas pruebas garantizan que este contrato inteligente se comporte de la manera que esperamos. Aquí hay un conjunto de pruebas completo que verifica todo el comportamiento del contrato inteligente:

var DappToken = artifacts.require("./DappToken.sol");

contract('DappToken', function(accounts) {
  var tokenInstance;

  it('initializes the contract with the correct values', function() {
    return DappToken.deployed().then(function(instance) {
      tokenInstance = instance;
      return tokenInstance.name();
    }).then(function(name) {
      assert.equal(name, 'DApp Token', 'has the correct name');
      return tokenInstance.symbol();
    }).then(function(symbol) {
      assert.equal(symbol, 'DAPP', 'has the correct symbol');
      return tokenInstance.standard();
    }).then(function(standard) {
      assert.equal(standard, 'DApp Token v1.0', 'has the correct standard');
    });
  })

  it('allocates the initial supply upon deployment', function() {
    return DappToken.deployed().then(function(instance) {
      tokenInstance = instance;
      return tokenInstance.totalSupply();
    }).then(function(totalSupply) {
      assert.equal(totalSupply.toNumber(), 1000000, 'sets the total supply to 1,000,000');
      return tokenInstance.balanceOf(accounts[0]);
    }).then(function(adminBalance) {
      assert.equal(adminBalance.toNumber(), 1000000, 'it allocates the initial supply to the admin account');
    });
  });

  it('transfers token ownership', function() {
    return DappToken.deployed().then(function(instance) {
      tokenInstance = instance;
      // Test `require` statement first by transferring something larger than the sender's balance
      return tokenInstance.transfer.call(accounts[1], 99999999999999999999999);
    }).then(assert.fail).catch(function(error) {
      assert(error.message.indexOf('revert') >= 0, 'error message must contain revert');
      return tokenInstance.transfer.call(accounts[1], 250000, { from: accounts[0] });
    }).then(function(success) {
      assert.equal(success, true, 'it returns true');
      return tokenInstance.transfer(accounts[1], 250000, { from: accounts[0] });
    }).then(function(receipt) {
      assert.equal(receipt.logs.length, 1, 'triggers one event');
      assert.equal(receipt.logs[0].event, 'Transfer', 'should be the "Transfer" event');
      assert.equal(receipt.logs[0].args._from, accounts[0], 'logs the account the tokens are transferred from');
      assert.equal(receipt.logs[0].args._to, accounts[1], 'logs the account the tokens are transferred to');
      assert.equal(receipt.logs[0].args._value, 250000, 'logs the transfer amount');
      return tokenInstance.balanceOf(accounts[1]);
    }).then(function(balance) {
      assert.equal(balance.toNumber(), 250000, 'adds the amount to the receiving account');
      return tokenInstance.balanceOf(accounts[0]);
    }).then(function(balance) {
      assert.equal(balance.toNumber(), 750000, 'deducts the amount from the sending account');
    });
  });

  it('approves tokens for delegated transfer', function() {
    return DappToken.deployed().then(function(instance) {
      tokenInstance = instance;
      return tokenInstance.approve.call(accounts[1], 100);
    }).then(function(success) {
      assert.equal(success, true, 'it returns true');
      return tokenInstance.approve(accounts[1], 100, { from: accounts[0] });
    }).then(function(receipt) {
      assert.equal(receipt.logs.length, 1, 'triggers one event');
      assert.equal(receipt.logs[0].event, 'Approval', 'should be the "Approval" event');
      assert.equal(receipt.logs[0].args._owner, accounts[0], 'logs the account the tokens are authorized by');
      assert.equal(receipt.logs[0].args._spender, accounts[1], 'logs the account the tokens are authorized to');
      assert.equal(receipt.logs[0].args._value, 100, 'logs the transfer amount');
      return tokenInstance.allowance(accounts[0], accounts[1]);
    }).then(function(allowance) {
      assert.equal(allowance.toNumber(), 100, 'stores the allowance for delegated trasnfer');
    });
  });

  it('handles delegated token transfers', function() {
    return DappToken.deployed().then(function(instance) {
      tokenInstance = instance;
      fromAccount = accounts[2];
      toAccount = accounts[3];
      spendingAccount = accounts[4];
      // Transfer some tokens to fromAccount
      return tokenInstance.transfer(fromAccount, 100, { from: accounts[0] });
    }).then(function(receipt) {
      // Approve spendingAccount to spend 10 tokens form fromAccount
      return tokenInstance.approve(spendingAccount, 10, { from: fromAccount });
    }).then(function(receipt) {
      // Try transferring something larger than the sender's balance
      return tokenInstance.transferFrom(fromAccount, toAccount, 9999, { from: spendingAccount });
    }).then(assert.fail).catch(function(error) {
      assert(error.message.indexOf('revert') >= 0, 'cannot transfer value larger than balance');
      // Try transferring something larger than the approved amount
      return tokenInstance.transferFrom(fromAccount, toAccount, 20, { from: spendingAccount });
    }).then(assert.fail).catch(function(error) {
      assert(error.message.indexOf('revert') >= 0, 'cannot transfer value larger than approved amount');
      return tokenInstance.transferFrom.call(fromAccount, toAccount, 10, { from: spendingAccount });
    }).then(function(success) {
      assert.equal(success, true);
      return tokenInstance.transferFrom(fromAccount, toAccount, 10, { from: spendingAccount });
    }).then(function(receipt) {
      assert.equal(receipt.logs.length, 1, 'triggers one event');
      assert.equal(receipt.logs[0].event, 'Transfer', 'should be the "Transfer" event');
      assert.equal(receipt.logs[0].args._from, fromAccount, 'logs the account the tokens are transferred from');
      assert.equal(receipt.logs[0].args._to, toAccount, 'logs the account the tokens are transferred to');
      assert.equal(receipt.logs[0].args._value, 10, 'logs the transfer amount');
      return tokenInstance.balanceOf(fromAccount);
    }).then(function(balance) {
      assert.equal(balance.toNumber(), 90, 'deducts the amount from the sending account');
      return tokenInstance.balanceOf(toAccount);
    }).then(function(balance) {
      assert.equal(balance.toNumber(), 10, 'adds the amount from the receiving account');
      return tokenInstance.allowance(fromAccount, spendingAccount);
    }).then(function(allowance) {
      assert.equal(allowance.toNumber(), 0, 'deducts the amount from the allowance');
    });
  });
});

Puede ejecutar la prueba desde la línea de comando con truffle como esta:

$ truffle test

Contrato inteligente de venta masiva

Ahora podemos construir un contrato inteligente de venta colectiva que permitirá a los inversores comprar tokens en una oferta inicial de monedas (ICO). Aquí está el código completo de solidez del contrato inteligente de venta colectiva:

pragma solidity ^0.4.2;

import "./DappToken.sol";

contract DappTokenSale {
    address admin;
    DappToken public tokenContract;
    uint256 public tokenPrice;
    uint256 public tokensSold;

    event Sell(address _buyer, uint256 _amount);

    function DappTokenSale(DappToken _tokenContract, uint256 _tokenPrice) public {
        admin = msg.sender;
        tokenContract = _tokenContract;
        tokenPrice = _tokenPrice;
    }

    function multiply(uint x, uint y) internal pure returns (uint z) {
        require(y == 0 || (z = x * y) / y == x);
    }

    function buyTokens(uint256 _numberOfTokens) public payable {
        require(msg.value == multiply(_numberOfTokens, tokenPrice));
        require(tokenContract.balanceOf(this) >= _numberOfTokens);
        require(tokenContract.transfer(msg.sender, _numberOfTokens));

        tokensSold += _numberOfTokens;

        Sell(msg.sender, _numberOfTokens);
    }

    function endSale() public {
        require(msg.sender == admin);
        require(tokenContract.transfer(admin, tokenContract.balanceOf(this)));

        // Just transfer the balance to the admin
        admin.transfer(address(this).balance);
    }
}

Echemos un vistazo a lo que hace este contrato inteligente y cómo funciona como una venta colectiva:

  • Almacena una cuenta de administrador para la venta colectiva address admin.
  • Hace referencia al contrato inteligente de token ERC-20 DappToken public tokenContract.
  • Almacena el precio del token uint256 public tokenPrice.
  • Almacena la cantidad de tokens vendidos uint256 public tokensSold.
  • Implementa un sellevento para que los consumidores puedan recibir notificaciones cada vez que se vende un token.
  • Implementa una buyTokensfunción que permite a los usuarios comprar tokens en la venta colectiva.
  • Implementa una endSalefunción que permite a un administrador finalizar la venta colectiva y recolectar los fondos Ether que se recaudaron durante la venta.

También puede leer las pruebas de este contrato inteligente para descubrir más sobre cómo funciona. Estas pruebas garantizan que este contrato inteligente se comporte de la manera que esperamos. Aquí hay un conjunto de pruebas completo que verifica todo el comportamiento del contrato inteligente:

var DappToken = artifacts.require('./DappToken.sol');
  var DappTokenSale = artifacts.require('./DappTokenSale.sol');

  contract('DappTokenSale', function(accounts) {
    var tokenInstance;
    var tokenSaleInstance;
    var admin = accounts[0];
    var buyer = accounts[1];
    var tokenPrice = 1000000000000000; // in wei
    var tokensAvailable = 750000;
    var numberOfTokens;

    it('initializes the contract with the correct values', function() {
      return DappTokenSale.deployed().then(function(instance) {
        tokenSaleInstance = instance;
        return tokenSaleInstance.address
      }).then(function(address) {
        assert.notEqual(address, 0x0, 'has contract address');
        return tokenSaleInstance.tokenContract();
      }).then(function(address) {
        assert.notEqual(address, 0x0, 'has token contract address');
        return tokenSaleInstance.tokenPrice();
      }).then(function(price) {
        assert.equal(price, tokenPrice, 'token price is correct');
      });
    });

    it('facilitates token buying', function() {
      return DappToken.deployed().then(function(instance) {
        // Grab token instance first
        tokenInstance = instance;
        return DappTokenSale.deployed();
      }).then(function(instance) {
        // Then grab token sale instance
        tokenSaleInstance = instance;
        // Provision 75% of all tokens to the token sale
        return tokenInstance.transfer(tokenSaleInstance.address, tokensAvailable, { from: admin })
      }).then(function(receipt) {
        numberOfTokens = 10;
        return tokenSaleInstance.buyTokens(numberOfTokens, { from: buyer, value: numberOfTokens * tokenPrice })
      }).then(function(receipt) {
        assert.equal(receipt.logs.length, 1, 'triggers one event');
        assert.equal(receipt.logs[0].event, 'Sell', 'should be the "Sell" event');
        assert.equal(receipt.logs[0].args._buyer, buyer, 'logs the account that purchased the tokens');
        assert.equal(receipt.logs[0].args._amount, numberOfTokens, 'logs the number of tokens purchased');
        return tokenSaleInstance.tokensSold();
      }).then(function(amount) {
        assert.equal(amount.toNumber(), numberOfTokens, 'increments the number of tokens sold');
        return tokenInstance.balanceOf(buyer);
      }).then(function(balance) {
        assert.equal(balance.toNumber(), numberOfTokens);
        return tokenInstance.balanceOf(tokenSaleInstance.address);
      }).then(function(balance) {
        assert.equal(balance.toNumber(), tokensAvailable - numberOfTokens);
        // Try to buy tokens different from the ether value
        return tokenSaleInstance.buyTokens(numberOfTokens, { from: buyer, value: 1 });
      }).then(assert.fail).catch(function(error) {
        assert(error.message.indexOf('revert') >= 0, 'msg.value must equal number of tokens in wei');
        return tokenSaleInstance.buyTokens(800000, { from: buyer, value: numberOfTokens * tokenPrice })
      }).then(assert.fail).catch(function(error) {
        assert(error.message.indexOf('revert') >= 0, 'cannot purchase more tokens than available');
      });
    });

    it('ends token sale', function() {
      return DappToken.deployed().then(function(instance) {
        // Grab token instance first
        tokenInstance = instance;
        return DappTokenSale.deployed();
      }).then(function(instance) {
        // Then grab token sale instance
        tokenSaleInstance = instance;
        // Try to end sale from account other than the admin
        return tokenSaleInstance.endSale({ from: buyer });
      }).then(assert.fail).catch(function(error) {
        assert(error.message.indexOf('revert' >= 0, 'must be admin to end sale'));
        // End sale as admin
        return tokenSaleInstance.endSale({ from: admin });
      }).then(function(receipt) {
        return tokenInstance.balanceOf(admin);
      }).then(function(balance) {
        assert.equal(balance.toNumber(), 999990, 'returns all unsold dapp tokens to admin');
        // Check that the contract has no balance
        balance = web3.eth.getBalance(tokenSaleInstance.address)
        assert.equal(balance.toNumber(), 0);
      });
    });
  });

Hasta aquí tienes los fundamentos básicos para la creación de un token dentro de la red de ethereum.

Artículos Relacionados

Artículos Destacados

4 comentarios en «Cómo crear un token ERC-20 en la red Ethereum»

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.