Cómo programar/crear tu propio token ERC20: explicación paso a paso en Remix

Programar y crear un token ERC-20 es relativamente sencillo, gracias a que el estándar está pensado para facilitar el trabajo a los desarrolladores. Los tokens ERC-20 son contratos inteligentes programados sobre el lenguaje Solidity que se ejecutan en la blockchain de Ethereum. Así, aunque son necesarias algunas nociones de programación, la infraestructura está pensada para que el máximo número de personas posible puedan acceder crear tokens. De hecho, en la actualidad existen cerca de 500.000 tokens ERC-20 diferentes.

Cómo programar y crear token ERC-20 paso a paso Remix

Una de las ventajas del lenguaje de programación Solidity es que permite programar sin instalar ningún programa en el ordenador. Así, para escribir el código de nuestro token utilizaremos la interfaz web Remix. Un entorno de desarrollo de Ethereum que permite compilar y desplegar contratos inteligentes en una máquina virtual. Una vez hemos accedido a Remix, seleccionamos la opción de crear una nueva carpeta y un nuevo archivo. Ponemos un nombre y ya podemos comenzar a escribir el contrato.

Versión del Compilador

Lo primero hay que seleccionar la versión del compilador. Una especie de traductor que convierte el lenguaje de alto nivel en lenguaje de máquina. En Remix podemos encontrar la información del compilador pinchando en el segundo símbolo de la columna de la izquierda (Solidity Compiler).

 

La versión del compilador que seleccionemos no importa tanto. No obstante, sí es muy importante que especifiquemos la versión del compilador que hemos seleccionado a la hora de escribir el contrato. Por eso, lo primero que debemos escribir en la creación de nuestro token es la especificación de la versión o un rango de versiones. En mi caso, al estar utilizando la versión 0.6.10, escribiré:

pragma solidity >=0.5.5<0.8.0;

Es decir, seguido de pragma solidity, escribimos un rango donde se encuentre la versión seleccionada. Además, es importante que cuando escribamos cada comando lo cerremos con un punto y coma (;).

A continuación, debemos especificar que vamos a usar la versión del codificador ABI v2, ya que esta segunda versión permite que estructuras y variables dinámicas pasen a funciones y sean devueltas por funciones y emitidas por eventos. Esta especificación la debemos implementar si estamos utilizando una versión del compilador anterior a la 0.8.0, ya que a partir de esta versión, la versión 2 del codificador ABI ya está incluida por defecto. En caso contrario, debemos escribir:

pragma experimental ABIEncoderV2;

Asimismo, es también importante indicar al principio del todo la licencia de uso. Para esto, en la primera línea del código introduciremos lo siguiente:

// SPDX-License-Identifier: MIT

Safe Math

El siguiente paso será asegurarse de usar operaciones matemáticas seguras que lancen excepciones en caso de que el resultado de un cálculo no sea el correcto. Para esto, definimos la librería “SafeMath” implementando tres operaciones: suma, resta y multiplicación. De esta manera, evitamos problemas como el “overflow” o “underflow”, que podrían dar paso a posibles hackeos. Estos problemas se dan cuando operaciones aritméticas sobrepasan los mínimos o máximos tamaños de los tipos de variable declaradas. Para definir la librería SafeMath, debemos escribir lo siguiente:

library SafeMath{
// La Resta
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a – b;
}

// La Suma
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}

// La Multiplicación
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}

uint256 c = a * b;
require(c / a == b, “SafeMath: multiplication overflow”);

return c;
}
}

Las 6 funciones clave

Ahora ya podemos comenzar a escribir las funciones definidas por el estándar ERC20. Un set de 6 funciones obligatorias para la creación del token y su posterior integración en marketplaces y criptocarteras. Todos los token ERC20 tienen como mínimo estas 6 funciones:

  1. totalSupply: Esta función específica el número total del suministro de tokens. Una vez este límite es alcanzado, el contrato rechazará crear más tokens
  2. balanceOf: Esta función sirve para que el contrato te devuelva el número de tokens que tiene una cartera.
  3. allowance: Función que verifica si un usuario tiene la cantidad suficiente de tokens para que los pueda enviar a otro usuario.
  4. transfer: La función que permite obtener una cantidad de tokens del suministro inicial y dárselos a un usuario.
  5. approve: Función que verifica si el contrato puede mandar una cantidad de tokens a un usuario teniendo en cuenta el suministro total de tokens.
  6. transferFrom: Esta función habilita la transferencia de tokens entre diferentes usuarios.

Además de esto, implementaremos 2 eventos adicionales. Un primer evento que notifique cuando una cantidad de tokens pase de un origen a un destino y un segundo evento que notifique la aprobación de la función allowance.

¿Cómo está transformando Blockchain y las DAO el mercado de trabajo?

Comenzamos a escribirlo

A continuación, escribimos estas funciones en la interfaz de nuestro token. Aquí, cabe señalar que el texto que va después de las dos barras (//) sirve únicamente para señalar que se está haciendo y que se entienda mejor.

//Interface token ERC
interface IERC20{
//El suministro total de tokens
function totalsupply()external view returns (uint256);
//Devuelve el número de tokens de una dirección
function balanceOf(address account)external view returns (uint256);
//Si un usuario tiene la cantidad de tokens suficientes (y devuelve el número)
function allowance(address owner, address spender)external view returns (uint256);
//Tokens del suministro inicial a un usuario
function transfer(address recipient, uint256 amount) external returns (bool);
//Si el contrato puede mandar una cantidad de tokens a un usuario
function approve(address spender, uint256 amount) external returns (bool);
//Habilita la transferencia de tokens entre usuarios
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
//Evento número 1
event Transfer(address indexed from, address indexed to, uint256 value);
//Evento número 2
event Approval(address indexed owner, address indexed spender, uint256 value);
}

Implementación del Token

El siguiente paso es implementar las funciones del token ERC20 y ponerle el nombre que queramos. Para comenzar la implementación del token, escribiremos lo siguiente: contract ERC20Basic is IERC20{

Una vez abierto este corchete, empezaremos con la implementación. Lo primero será ponerle un nombre a nuestro token y un acrónimo con el que se le conocerá, así como establecer los decimales con los que se quiere trabajar. En este caso he decidido nombrar al token “Observatorio Blockchain Prueba”, pero entre estas comillas se puede introducir cualquier nombre. Lo mismo ocurre con los decimales y el acrónimo.
string public constant name = “Observatorio Blockchain Prueba”;
string public constant symbol =”OBP”;
uint public constant decimals= 2;
A continuación, implementamos los dos eventos e indicamos la utilización de la librería de SafeMath del inicio para garantizar la consistencia del token.
event Transfer(address indexed from, address indexed to, uint256 tokens);
event Approval(address indexed owner, address indexed spender, uint256 tokens);
using SafeMath for uint256;

Mapping y constructor

El siguiente paso es construir dos mappings. Un primer mapping que indique que a cada dirección le va a corresponder una cantidad de tokens determinada, y un segundo mapping que indique que a cada dirección le corresponde un conjunto de direcciones con cantidad en cada una de ellas. Este segundo mapping sirve para ver que pese a que los tokens puede haberlos  minado una sola persona, pueden distribuirse entre diferentes usuarios y así sucesivamente.

mapping(address=>uint) balances;
mapping (address => mapping (address => uint)) allowed;
uint256 totalSupply_;
A continuación hay que definir el constructor. El constructor tomará la cantidad de tokens que se quieren crear inicialmente y se los dará al creador original de estos tokens (nosotros). Además, define que el constructor será el único que puede determinar la cantidad total de tokens.
constructor (uint256 initialSupply) public{
    totalSupply_ = initialSupply;
    balances[msg.sender]=totalSupply_;
}

Implementación de las funciones

El siguiente paso es escribir la implementación de las 6 funciones que hemos introducido previamente y la lógica de cada una de ellas. Copiando y pegando en Remix las especificaciones siguientes, habríamos terminado de escribir el código del token.

function totalsupply() public override view returns (uint256){
 return totalSupply_;
}
function increaseTotalSupply(uint newTokensAmount) public {
    totalSupply_+=newTokensAmount;
    balances[msg.sender]+= newTokensAmount;
}
function balanceOf(address tokenOwner) public override view returns (uint256){
    return balances[tokenOwner];
    }
    function allowance(address owner, address delegate) public override view returns (uint256){
        return allowed[owner][delegate];
    }
    function transfer(address recipient, uint256 numTokens) public override returns (bool){
        require(numTokens <=balances[msg.sender]);
        balances[msg.sender]=balances[msg.sender]. sub(numTokens);
        balances[recipient]=balances[recipient].add(numTokens);
        emit Transfer(msg.sender, recipient, numTokens);
        return true;
    }
    function approve(address delegate, uint256 numTokens) public override returns (bool){
        allowed[msg.sender][delegate]=numTokens;
        emit Approval(msg.sender, delegate, numTokens);
        return true;
    }
    function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool){
        require(numTokens <= balances[owner]);
        require(numTokens <= allowed[owner][msg.sender]);
        balances[owner]=balances[owner].sub(numTokens);
        allowed[owner][msg.sender]=allowed[owner][msg.sender].sub(numTokens);
        balances[buyer]=balances[buyer].add(numTokens);
        emit Transfer (owner, buyer, numTokens);
        return true;
    }
}

Una vez hemos escrito todo el código en Remix, debemos asegurarnos que en la parte del compilador aparece un tick verde. Este tick verde es la prueba de que todas las funciones están escritas correctamente. Si por el contrario, nos mostrara algún fallo, el propio Remix nos indica dónde tenemos ese fallo. Los errores más comunes suelen ocurrir por pequeños fallos a la hora de escribir el código.

Despliegue

Para comprobar que todo funcione bien y probar a desplegar el token, debemos ir a la columna izquierda a la parte de despliegue (deploy). Aquí, en “Account” será la cuenta principal que reciba todos los tokens iniciales. Así, para comprobar que todo este bien, debemos indicar la cantidad de tokens que queremos desplegar al lado del apartado donde pone “Deploy”, y darle al cuadrado para ver si todo está bien. Si todo está correcto, en la pantalla de abajo nos aparecerá un tick verde seguido de la dirección donde se han desplegado los tokens.

Una vez desplegado, en “Deployed Contracts” podemos ver nuestro token y si pinchamos en la flecha de la izquierda nos aparecerá un desplegable para probar que las funciones de nuestro token son correctas. Por ejemplo, si pinchamos en nombre, nos deberá aparecer el nombre de nuestro token y en Total Supply nos deberá aparecer el suministro de tokens. Además, en balanceOf podremos comprobar cuantos tokens tienen cada una de las cuentas. Por ejemplo, si copiamos y pegamos la dirección inicial, su balance debe ser el suministro total de tokens ya que no hemos hecho ninguna transacción.

Comprobaciones

Para comprobar que funcionen las transferencias, en transferFrom incluimos la dirección principal (la primera del apartado de Account) y probamos a transferirlo colocando en el apartado transfer la cuenta que va a recibir esos tokens (otra de las cuentas del desplegable “accounts”) seguido de la cantidad de tokens que queremos transferir. Si pulsamos en transfer, en la parte inferior izquierda nos debe aparecer un tick verde que indique que se ha transferido correctamente, y en balanceOf, el número de tokens de cada una de las cuentas se debe haber modificado.

Una vez hemos comprobado que todo funciona correctamente, el token ya estaría programado y listo para ser lanzado a una blockchain pública, como la BSC o la red de Polygon (MATIC). Para implementar el token en una de estas dos redes habría que pagar gas. No obstante, en un próximo artículo explicaré como implementar los tokens de manera gratuita en una red de prueba.

Código entero para copiar y pegar:

// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
library SafeMath{
// La Resta
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a – b;
}
// La Suma
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
// La Multiplicación
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, “SafeMath: multiplication overflow”);
return c;
}
}
//Interface token ERC
interface IERC20{
//El suministro total de tokens
function totalsupply()external view returns (uint256);
//Devuelve el número de tokens de una dirección
function balanceOf(address account)external view returns (uint256);
//Un usuario tiene la cantidad de tokens suficientes (y devuelve el número)
function allowance(address owner, address spender)external view returns (uint256);
//Tokens del suministro inicial a un usuario
function transfer(address recipient, uint256 amount) external returns (bool);
//Si el contrato puede mandar una cantidad de tokens a un usuario
function approve(address spender, uint256 amount) external returns (bool);
//Habilita la transferencia de tokens entre usuarios
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
//Evento número 1
event Transfer(address indexed from, address indexed to, uint256 value);
//Evento número 2
event Approval(address indexed owner, address indexed spender, uint256 value);
}
//Implementacion funciones token ERC20
contract ERC20Basic is IERC20{
string public constant name = “Observatorio Blockchain Prueba”;
string public constant symbol =”OBP”;
uint public constant decimals= 2;
event Transfer(address indexed from, address indexed to, uint256 tokens);
event Approval(address indexed owner, address indexed spender, uint256 tokens);
using SafeMath for uint256;
mapping(address=>uint) balances;
mapping (address => mapping (address => uint)) allowed;
uint256 totalSupply_;
constructor (uint256 initialSupply) public{
    totalSupply_ = initialSupply;
    balances[msg.sender]=totalSupply_;
}
function totalsupply() public override view returns (uint256){
 return totalSupply_;
}
function increaseTotalSupply(uint newTokensAmount) public {
    totalSupply_+=newTokensAmount;
    balances[msg.sender]+= newTokensAmount;
}
function balanceOf(address tokenOwner) public override view returns (uint256){
    return balances[tokenOwner];
    }
    function allowance(address owner, address delegate) public override view returns (uint256){
        return allowed[owner][delegate];
    }
    function transfer(address recipient, uint256 numTokens) public override returns (bool){
        require(numTokens <=balances[msg.sender]);
        balances[msg.sender]=balances[msg.sender]. sub(numTokens);
        balances[recipient]=balances[recipient].add(numTokens);
        emit Transfer(msg.sender, recipient, numTokens);
        return true;
    }
    function approve(address delegate, uint256 numTokens) public override returns (bool){
        allowed[msg.sender][delegate]=numTokens;
        emit Approval(msg.sender, delegate, numTokens);
        return true;
    }
    function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool){
        require(numTokens <= balances[owner]);
        require(numTokens <= allowed[owner][msg.sender]);
        balances[owner]=balances[owner].sub(numTokens);
        allowed[owner][msg.sender]=allowed[owner][msg.sender].sub(numTokens);
        balances[buyer]=balances[buyer].add(numTokens);
        emit Transfer (owner, buyer, numTokens);
        return true;
    }
}

También puedes seguirnos en nuestros canales de Telegram Twitter

Guillermo Callejo
Comparte esto:
Esta web utiliza cookies. Puedes ver aquí la Política de Cookies. Si continuas navegando estás aceptándola   
Privacidad