Cómo: Crear un token en la red PIRL

Compartir esta publicacion

Compartir en facebook
Compartir en linkedin
Compartir en twitter
Compartir en email

La moneda

Vamos a crear un token digital. Los tokens en el ecosistema Pirl pueden representar cualquier bien comercializable fungible: monedas, puntos de fidelidad, certificados de oro, pagarés, artículos en el juego, etc. Dado que todos los tokens implementan algunas características básicas de manera estándar, esto también significa que su token será instantáneamente compatible con la billetera Pirl y cualquier otro cliente o contrato que use los mismos estándares.

TOKEN VIABLE MÍNIMO

El contrato de token estándar puede ser bastante complejo. Pero, en esencia, una ficha muy básica se reduce a esto:

contract MyToken {/ * Esto crea una matriz con todos los saldos * / mapping (address => uint256) public balanceOf; / * Inicializa el contrato con tokens de suministro iniciales para el creador del contrato * / function MyToken (uint256 initialSupply) {balanceOf [msg.sender] = initialSupply; // Entregue al creador todos los tokens iniciales} / * Enviar monedas * / transferencia de función (dirección _to, uint256 _value) {require (balanceOf [msg.sender]> = _value); // Comprueba si el remitente tiene suficiente require (balanceOf [_to] + _value> = balanceOf [_to]); // Verificar desbordamientos balanceOf [msg.sender] - = _value; // Restar del remitente balanceOf [_to] + = _value; // Añadir lo mismo al destinatario}}

EL CÓDIGO

Pero si solo quieres copiar y pegar un código más completo, entonces usa esto:

solidez de pragma ^ 0.4.16;

interface tokenRecipient {function acceptApproval (address _from, uint256 _value, address _token, bytes _extraData) public; }

TokenERC20 de contrato {
   // Variables públicas del token
   cadena de nombre público;
   símbolo público de cadena;
   uint8 decimales públicos = 18;
   // 18 decimales es el valor predeterminado altamente sugerido, evite cambiarlo
   uint256 public totalSupply;

   // Esto crea una matriz con todos los saldos
   mapping (address => uint256) public balanceOf;
   asignación (dirección => asignación (dirección => uint256)) asignación pública;

   // Esto genera un evento público en la cadena de bloques que notificará a los clientes
   Transferencia de eventos (dirección indexada desde, dirección indexada a, valor uint256);

   // Esto notifica a los clientes sobre la cantidad quemada
   evento Burn (dirección indexada desde, valor uint256);

   /**
    * Función constructora
    *
    * Inicializa el contrato con tokens de suministro iniciales para el creador del contrato
    */
   función TokenERC20 (
       uint256 initialSupply,
       string tokenName,
       token de cadena
   ) público {
       totalSupply = initialSupply * 10 ** uint256 (decimales); // Actualizar el suministro total con la cantidad decimal
       balanceOf [msg.sender] = totalSupply; // Dale al creador todos los tokens iniciales
       nombre = tokenName; // Establecer el nombre para mostrar
       símbolo = tokenSymbol; // Establecer el símbolo para mostrar
   }

   /**
    * Transferencia interna, solo puede ser convocada por este contrato
    */
   función _transfer (dirección _de, dirección _to, uint _valor) interna {
       // Evita la transferencia a la dirección 0x0. Use burn () en su lugar
       require (_to! = 0x0);
       // Comprueba si el remitente tiene suficiente
       require (balanceOf [_from]> = _value);
       // Verificar desbordamientos
       require (balanceOf [_to] + _value> balanceOf [_to]);
       // Guardar esto para una afirmación en el futuro
       uint previousBalances = balanceOf [_from] + balanceOf [_to];
       // Restar del remitente
       balanceOf [_from] - = _value;
       // Añadir lo mismo al destinatario
       balanceOf [_to] + = _value;
       Transferencia (_from, _to, _value);
       // Las afirmaciones se usan para usar análisis estático para encontrar errores en su código. Nunca deberían fallar
       afirmar (balanceOf [_from] + balanceOf [_to] == anterioresBalances);
   }

   /**
    * Transferir tokens
    *
    * Envíe tokens `_value` a` _to` desde su cuenta
    *
    * @param _to La dirección del destinatario
    * @param _value la cantidad a enviar
    */
   transferencia de funciones (dirección _to, uint256 _valor) público {
       _transfer (msg.sender, _to, _value);
   }

   /**
    * Transferir tokens desde otra dirección
    *
    * Enviar tokens `_value` a` _to` en nombre de `_from`
    *
    * @param _from La dirección del remitente
    * @param _to La dirección del destinatario
    * @param _value la cantidad a enviar
    */
   función transferFrom (address _from, address _to, uint256 _value) devoluciones públicas (éxito de bool) {
       require (_value <= asignación [_from] [msg.sender]); // Verificar la asignación
       asignación [_from] [msg.sender] - = _value;
       _transfer (_from, _to, _value);
       volver verdadero;
   }

   /**
    * Establecer asignación para otra dirección
    *
    * Permite a `_spender` gastar no más de` _value` tokens en tu nombre
    *
    * @param _spender La dirección autorizada para gastar
    * @param _value la cantidad máxima que pueden gastar
    */
   función aprobar (dirección _spender, uint256 _value) public
       retornos (éxito de bool) {
       asignación [msg.sender] [_ spender] = _value;
       volver verdadero;
   }

   /**
    * Establecer asignación para otra dirección y notificar
    *
    * Permite que `_spender` gaste no más de` _value` tokens en su nombre, y luego haga ping al contrato al respecto
    *
    * @param _spender La dirección autorizada para gastar
    * @param _value la cantidad máxima que pueden gastar
    * @param _extraData alguna información adicional para enviar al contrato aprobado
    */
   función approveAndCall (dirección _spender, uint256 _value, bytes _extraData)
       público
       retornos (éxito de bool) {
       tokenRecipient spender = tokenRecipient (_spender);
       if (aprobar (_spender, _value)) {
           spender.receiveApproval (msg.sender, _value, this, _extraData);
           volver verdadero;
       }
   }

   /**
    * Destruye fichas
    *
    * Eliminar los tokens `_value` del sistema de forma irreversible
    *
    * @param _value la cantidad de dinero para quemar
    */
   función burn (uint256 _value) retornos públicos (éxito de bool) {
       require (balanceOf [msg.sender]> = _value); // Comprueba si el remitente tiene suficiente
       balanceOf [msg.sender] - = _value; // Restar del remitente
       totalSupply - = _value; // Actualizaciones totalSupply
       Grabar (msg.sender, _value);
       volver verdadero;
   }

   /**
    * Destruye tokens de otra cuenta
    *
    * Elimine los tokens `_value` del sistema de forma irreversible en nombre de` _from`.
    *
    * @param _de la dirección del remitente
    * @param _value la cantidad de dinero para quemar
    */
   función burnFrom (dirección _from, uint256 _value) devoluciones públicas (éxito de bool) {
       require (balanceOf [_from]> = _value); // Comprueba si el saldo objetivo es suficiente
       require (_value <= asignación [_from] [msg.sender]); // Verificar la asignación
       balanceOf [_from] - = _value; // Restar del saldo objetivo
       asignación [_from] [msg.sender] - = _value; // Restar de la asignación del remitente
       totalSupply - = _value; // Actualizar totalSupply
       Grabar (_de, _valor);
       volver verdadero;
   }
}

Entendiendo el Código

Entonces, comencemos con lo básico. Abra la aplicación Wallet, vaya a la pestaña Contratos y luego Implemente un nuevo contrato. En el campo de texto del código fuente del contrato de solidez, escriba el código a continuación:

contrato MyToken {
       / * Esto crea una matriz con todos los saldos * /
       mapping (address => uint256) public balanceOf;
   }

Una asignación significa una matriz asociativa, donde se asocian direcciones con saldos. Las direcciones están en el formato básico hexadecimal Pirl, mientras que los saldos son enteros, que van de 0 a 115 quattuorvigintillion. Si no sabe cuánto es un quattuorvigintillion, es muchos vigintillones más que nada para lo que planea usar sus tokens. La palabra clave pública significa que cualquier persona en la cadena de bloques podrá acceder a esta variable, lo que significa que todos los saldos son públicos (como deben ser, para que los clientes los muestren).

Si publicara su contrato de inmediato, funcionaría pero no sería muy útil: sería un contrato que podría consultar el saldo de su moneda para cualquier dirección, pero como nunca creó una sola moneda, cada una de ellas return 0. Entonces vamos a crear algunos tokens al inicio. Agregue este código antes del último corchete de cierre, justo debajo de la línea de mapeo.

función MyToken () {
       balanceOf [msg.sender] = 21000000;
   }

Observe que la función MyToken tiene el mismo nombre que el contrato MyToken. Esto es muy importante y si cambia el nombre de uno, también debe cambiar el nombre del otro: esta es una función especial de inicio que se ejecuta solo una vez y solo cuando el contrato se carga por primera vez en la red. Esta función establecerá el saldo de msg.sender, el usuario que implementó el contrato, con un saldo de 21 millones.

La elección de 21 millones fue bastante arbitraria, y puede cambiarla a lo que desee en el código, pero hay una mejor manera: en su lugar, proporciónela como un parámetro para la función, así:

función MyToken (uint256 initialSupply) public {
       balanceOf [msg.sender] = initialSupply;
   }

Eche un vistazo a la columna derecha al lado del contrato y verá una lista desplegable, escrita para elegir un contrato. Seleccione el contrato "MyToken" y verá que ahora muestra una sección llamada Parámetros del constructor. Estos son parámetros modificables para su token, por lo que puede reutilizar el mismo código y solo cambiar estas variables en el futuro.

En este momento tiene un contrato funcional que creó saldos de tokens, pero como no hay ninguna función para moverlo, todo lo que hace es permanecer en la misma cuenta. Así que vamos a implementar eso ahora. Escriba el siguiente código antes del último paréntesis.

/ * Enviar monedas * /
   transferencia de funciones (dirección _to, uint256 _valor) {
       / * Sumar y restar saldos nuevos * /
       balanceOf [msg.sender] - = _value;
       balanceOf [_to] + = _value;
   }

Esta es una función muy sencilla: tiene un destinatario y un valor como parámetro y cada vez que alguien lo llame, restará el _valor de su saldo y lo agregará al _al saldo. Inmediatamente hay un problema obvio: ¿qué sucede si la persona quiere enviar más de lo que posee? Dado que no queremos manejar la deuda en este contrato en particular, simplemente haremos una verificación rápida y si el remitente no tiene fondos suficientes, la ejecución del contrato simplemente se detendrá. También es para verificar si hay desbordamientos, para evitar tener un número tan grande que vuelva a ser cero.

Para detener la ejecución de un contrato a mitad de la ejecución, puede devolver o tirar. El primero costará menos gasolina, pero puede ser más dolor de cabeza, ya que cualquier cambio que haya realizado hasta ahora en el contrato se mantendrá. Por otro lado, 'tirar' cancelará toda la ejecución del contrato, revertirá cualquier cambio que la transacción pueda haber realizado y el remitente perderá todo el Pirl que envió por gasolina. Pero dado que Wallet puede detectar que se lanzará un contrato, siempre muestra una alerta, evitando así que se gaste cualquier Pirl.

transferencia de funciones (dirección _to, uint256 _valor) {
       / * Verifique si el remitente tiene saldo y si hay desbordamientos * /
       require (balanceOf [msg.sender]> = _value && balanceOf [_to] + _value> = balanceOf [_to]);

       / * Sumar y restar saldos nuevos * /
       balanceOf [msg.sender] - = _value;
       balanceOf [_to] + = _value;
   }

 

Ahora todo lo que falta es tener información básica sobre el contrato. En el futuro cercano, esto puede ser manejado por un registro de tokens, pero por ahora los agregaremos directamente al contrato:

cadena de nombre público;
símbolo público de cadena;
uint8 decimales públicos;

Y ahora actualizamos la función de constructor para permitir que todas esas variables se configuren al principio:

/ * Inicializa el contrato con tokens de suministro iniciales para el creador del contrato * /
   function MyToken (uint256 initialSupply, string tokenName, string tokenSymbol, uint8 decimalUnits) {
       balanceOf [msg.sender] = initialSupply; // Dale al creador todos los tokens iniciales
       nombre = tokenName; // Establecer el nombre para mostrar
       símbolo = tokenSymbol; // Establecer el símbolo para mostrar
       decimales = unidades decimales; // Cantidad de decimales para mostrar
   }

Finalmente, ahora necesitamos somPirling llamado Eventos. Estas son funciones especiales y vacías a las que llama para ayudar a clientes como Pirl Wallet a realizar un seguimiento de las actividades que se realizan en el contrato. Los eventos deben comenzar con una letra mayúscula. Agregue esta línea al comienzo del contrato para declarar el evento:

Transferencia de eventos (dirección indexada desde, dirección indexada a, valor uint256);
 

Y luego solo necesita agregar estas dos líneas dentro de la función de "transferencia":

      / * Notificar a cualquiera que escuche que esta transferencia se llevó a cabo * /
       Transferencia (msg.sender, _to, _value);

 

¡Y ahora tu ficha está lista!

Notado los comentarios?

¿Cuáles son esos comentarios de @notice y @param, podrías preguntar? Eso es Natspec Un estándar emergente para una especificación de lenguaje natural, que permite a las billeteras mostrar al usuario una descripción en lenguaje natural de lo que el contrato está por hacer. Aunque actualmente no es compatible con muchas billeteras, esto cambiará en el futuro, por lo que es bueno estar preparado.

Cómo implementar

Si aún no está allí, abra la Cartera Pirl, vaya a la pestaña de contratos y luego haga clic en "implementar nuevo contrato".

Ahora obtenga la fuente del token desde arriba y péguela en el "campo de fuente de Solidez". Si el código se compila sin ningún error, debería ver una lista desplegable "elegir un contrato" a la derecha. Consíguelo y selecciona el contrato "MyToken". En la columna derecha, verá todos los parámetros que necesita para personalizar su propio token. Puedes modificarlos como quieras.

Desplácese hasta el final de la página y verá una estimación del costo de cómputo de ese contrato y puede seleccionar una tarifa sobre cuánto Pirl está dispuesto a pagar por él. Se le devolverá cualquier exceso de Pirl que no gaste para que pueda dejar la configuración predeterminada si lo desea. Presione "desplegar", escriba la contraseña de su cuenta y espere unos segundos a que se recupere su transacción.

Serás redirigido a la página principal donde puedes ver tu transacción esperando confirmaciones. Haga clic en la cuenta y después de no más de un minuto debería ver que su cuenta mostrará que tiene 100% de las acciones que acaba de crear. Para enviar algunos a algunos amigos: seleccione "enviar", y luego elija la moneda que desea enviar (Pirl o su recurso compartido recién creado), pegue la dirección de su amigo en el campo "para" y presione "enviar".

Si se lo envía a un amigo, todavía no verá nada en su billetera. Esto se debe a que la billetera solo rastrea los tokens que conoce, y debe agregarlos manualmente. Ahora vaya a la pestaña "Contratos" y debería ver un enlace a su contrato recién creado. Haga clic en él para ir a su página. Dado que esta es una página de contrato muy simple, no hay mucho que hacer aquí, simplemente haga clic en "copiar dirección" y pegue la dirección del contrato en un editor de texto, lo necesitará en breve.

Para agregar un token para ver, vaya a la página de contratos y luego haga clic en "Ver token". Aparecerá una ventana emergente y solo necesita pegar la dirección del contrato. El nombre del token, el símbolo y el número decimal se deben completar automáticamente, pero si no es así, puede poner lo que quiera (solo afectará cómo se muestra en su billetera). Una vez que haga esto, automáticamente se le mostrará el saldo que tenga de ese token y podrá enviarlo a cualquier otra persona.

¡Y ahora tienes tu propio token criptográfico! Los tokens por sí mismos pueden ser útiles como intercambio de valor en las comunidades locales, formas de realizar un seguimiento de las horas trabajadas u otros programas de lealtad. ¿Pero podemos hacer que una moneda tenga un valor intrínseco haciéndola útil?

Mejora tu token

Puede implementar todo su token de cifrado sin tocar una línea de código, pero la verdadera magia ocurre cuando comienza a personalizarlo. Las siguientes secciones serán sugerencias sobre funciones que puede agregar a su token para que se ajuste más a sus necesidades.

Más funciones básicas

Notarás que hay más funciones en tu contrato de token básico, como aprobar, enviar y otras. Estas funciones están ahí para que su token interactúe con otros contratos: si quiere, por ejemplo, vender tokens a un intercambio descentralizado, solo enviarlos a una dirección no será suficiente ya que el intercambio no estará al tanto de los nuevos tokens o quién envió ellos, porque los contratos no pueden suscribirse a Eventos solo para funcionar llamadas. Entonces, para los contratos, primero debe aprobar una cantidad de tokens que pueden mover de su cuenta y luego hacerles ping para hacerles saber que deben hacer lo suyo, o hacer las dos acciones en una, con approveAndCall.

Debido a que muchas de estas funciones tienen que reimplementar la transferencia de tokens, tiene sentido cambiarlos a una función interna, que solo puede ser invocada por el propio contrato:

/ * Transferencia interna, solo se puede invocar mediante este contrato * /
   función _transfer (dirección _de, dirección _to, uint _valor) interna {
       require (_to! = 0x0); // Evita la transferencia a la dirección 0x0. Use burn () en su lugar
       require (balanceOf [_from]> = _value); // Comprueba si el remitente tiene suficiente
       require (balanceOf [_to] + _value> balanceOf [_to]); // Verificar desbordamientos
       require (! frozenAccount [_from]); // Comprueba si el remitente está congelado
       require (! frozenAccount [_to]); // Comprobar si el destinatario está congelado
       balanceOf [_from] - = _value; // Restar del remitente
       balanceOf [_to] + = _value; // Añadir lo mismo al destinatario
       Transferencia (_from, _to, _value);
   }

Ahora todas sus funciones que resultan en la transferencia de monedas, pueden hacer sus propios controles y luego llamar a la transferencia con los parámetros correctos. Tenga en cuenta que esta función moverá monedas de cualquier cuenta a otra, sin requerir el permiso de nadie para hacerlo: es por eso que es una función interna, solo llamada por el contrato: si agrega alguna función que lo llame, asegúrese de que verifique correctamente si la persona que llama debe tener permiso para moverlos.

Administrador centralizado

Todos los dapps están completamente descentralizados de forma predeterminada, pero eso no significa que no puedan tener algún tipo de administrador central, si así lo desea. Tal vez desee la capacidad de acuñar más monedas, tal vez desee prohibir que algunas personas usen su moneda. Puedes agregar cualquiera de esas características, pero la trampa es que solo puedes agregarlas al principio, por lo que todos los poseedores de fichas siempre sabrán exactamente las reglas del juego antes de que decidan tener una.

Para que eso suceda, necesita un controlador central de moneda. Esta podría ser una cuenta simple, pero también podría ser un contrato y, por lo tanto, la decisión de crear más tokens dependerá del contrato: si se trata de una organización democrática que puede votar, o tal vez sea solo una forma de limitar el poder del dueño del token.

Para hacerlo, aprenderemos una propiedad muy útil de los contratos: la herencia. La herencia permite que un contrato adquiera propiedades de un contrato principal, sin tener que redefinirlas todas. Esto hace que el código sea más limpio y fácil de reutilizar. Agregue este código a la primera línea de su código, antes de contratar MyToken {.

propiedad del contrato {
       dirección del propietario público;

       función propiedad () {
           propietario = msg.sender;
       }

       modificador onlyOwner {
           require (msg.sender == propietario);
           _;
       }

       función transferOwnership (dirección newOwner) onlyOwner {
           propietario = newOwner;
       }
   }

Esto crea un contrato muy básico que no hace nada excepto definir algunas funciones genéricas sobre un contrato que puede ser "propiedad". Ahora el siguiente paso es simplemente agregar el texto que pertenece a su contrato:

 contrato MyToken es propiedad de {
       / * el resto del contrato como siempre * /

Esto significa que todas las funciones dentro de MyToken ahora pueden acceder al propietario de la variable y al modificador onlyOwner. El contrato también tiene una función para transferir la propiedad. Dado que puede ser interesante establecer el propietario del contrato al inicio, también puede agregar esto a la función de constructor:

función MyToken (
       uint256 initialSupply,
       string tokenName,
       uint8 decimalUnits,
       token de cadena
       dirección centralMinter
       ) {
       if (centralMinter! = 0) propietario = centralMinter;
   }

Menta Central

Suponga que desea que cambie la cantidad de monedas en circulación. Este es el caso cuando sus tokens realmente representan un activo fuera de blockchain (como certificados de oro o monedas gubernamentales) y desea que el inventario virtual refleje el real. Este también podría ser el caso cuando los titulares de divisas esperan cierto control del precio del token y desean emitir o eliminar tokens de la circulación.

Primero, necesitamos agregar una variable para almacenar el totalSupply y asignarlo a nuestra función constructora.

contrato MyToken {
       uint256 public totalSupply;

       función MyToken (...) {
           totalSupply = initialSupply;
           …
       }
       …
   }

Ahora agreguemos una nueva función finalmente que permitirá al propietario crear nuevos tokens:

función mintToken (objetivo de dirección, uint256 mintedAmount) onlyOwner {
       balanceOf [target] + = mintedAmount;
       totalSupply + = mintedAmount;
       Transferencia (0, propietario, mintedAmount);
       Transferencia (propietario, objetivo, mintedAmount);
   }

Observe el modificador onlyOwner al final del nombre de la función. Esto significa que esta función se reescribirá en la compilación para heredar el código del modificador onlyOwner que habíamos definido antes. El código de esta función se insertará donde haya un subrayado en la función modificadora, lo que significa que esta función en particular solo puede ser invocada por la cuenta configurada como el propietario. Simplemente agregue esto a un contrato con un modificador de propietario y podrá crear más monedas.

Congelación de activos

Dependiendo de su caso de uso, es posible que deba tener algunos obstáculos regulatorios sobre quién puede y quién no puede usar sus tokens. Para que eso suceda, puede agregar un parámetro que permita al propietario del contrato congelar o descongelar activos.

Agregue esta variable y funcione en cualquier lugar dentro del contrato. Puede colocarlos en cualquier lugar, pero para una buena práctica, le recomendamos que coloque las asignaciones con las otras asignaciones y eventos con los otros eventos.

mapping (address => bool) public frozenAccount;
   evento FrozenFunds (dirección de destino, bool frozen);

   función freezeAccount (dirección de destino, bool freeze) onlyOwner {
       frozenAccount [target] = congelar;
       FrozenFunds (objetivo, congelación);
   }

Con este código, todas las cuentas se descongelan de manera predeterminada, pero el propietario puede establecer cualquiera de ellas en un estado de congelación llamando a Freeze Account. Desafortunadamente, la congelación no tiene ningún efecto práctico porque no hemos agregado nada a la función de transferencia. Estamos cambiando eso ahora:

transferencia de funciones (dirección _to, uint256 _valor) {
       require (! frozenAccount [msg.sender]);

Ahora, cualquier cuenta que esté congelada seguirá teniendo sus fondos intactos, pero no podrá moverlos. Todas las cuentas se descongelan de manera predeterminada hasta que las congela, pero puede revertir fácilmente ese comportamiento en una lista blanca donde necesita aprobar manualmente cada cuenta. Simplemente cambie el nombre de la Cuenta congelada a Cuenta aprobada y cambie la última línea a:

require (aprobóAccount [msg.sender]);

Venta y compra automáticas

Hasta ahora, ha confiado en la utilidad y la confianza para valorar su token. Pero si lo desea, puede hacer que el valor del token esté respaldado por Pirl (u otros tokens) creando un fondo que automáticamente los venda y los compre al valor de mercado.

Primero, fijemos el precio de compra y venta:

uint256 precio de venta público;
   uint256 public buyPrice;

   función setPrices (uint256 newSellPrice, uint256 newBuyPrice) onlyOwner {
       sellPrice = newSellPrice;
       buyPrice = newBuyPrice;
   }

Esto es aceptable para un precio que no cambia muy a menudo, ya que cada nuevo cambio de precio requerirá que ejecutes una transacción y gastes un poco de Pirl. Si desea tener un precio flotante constante, le recomendamos que investigue los feeds de datos estándar

El siguiente paso es realizar las funciones de compra y venta:

función buy () devoluciones por pagar (monto de la unidad) {
       cantidad = valor de msg / buyPrice; // calcula la cantidad
       require (balanceOf [this]> = monto); // comprueba si tiene suficiente para vender
       balanceOf [msg.sender] + = cantidad; // agrega el monto al saldo del comprador
       balanceOf [this] - = cantidad; // resta el monto del saldo del vendedor
       Transferencia (esto, msg.sender, cantidad); // ejecuta un evento que refleja el cambio
       cantidad de devolución; // finaliza la función y devuelve
   }

   función venta (monto de uint) retornos (ingreso de uint) {
       require (balanceOf [msg.sender]> = cantidad); // comprueba si el remitente tiene suficiente para vender
       balanceOf [this] + = cantidad; // agrega el monto al saldo del propietario
       balanceOf [msg.sender] - = cantidad; // resta la cantidad del saldo del vendedor
       ingresos = cantidad * precio de venta;
       msg.sender.transfer (ingresos); // envía Pirl al vendedor: es importante hacer esto al final para evitar ataques de recurrencia
       Transferencia (msg.sender, esto, cantidad); // ejecuta un evento que refleja el cambio
       retorno de ingresos; // finaliza la función y devuelve
   }

Tenga en cuenta que esto no creará nuevos tokens sino que cambiará el saldo que posee el contrato. El contrato puede contener tanto sus propios tokens como Pirl y el propietario del contrato, mientras que puede establecer precios o, en algunos casos, crear nuevos tokens (si corresponde), no puede tocar los tokens o Pirl del banco. La única forma en que este contrato puede mover fondos es vendiéndolos y comprándolos.

Nota Los "precios" de compra y venta no se establecen en Pirl, sino en wei, la moneda mínima del sistema (equivalente al centavo en euros y dólares, o el Satoshi en Bitcoin). Un Pirl es 1000000000000000000 wei. Entonces, al establecer los precios de su token en Pirl, agregue 18 ceros al final.

Al crear el contrato, envíele suficiente Pirl para que pueda volver a comprar todos los tokens en el mercado; de lo contrario, su contrato será insolvente y sus usuarios no podrán vender sus tokens.

Los ejemplos anteriores, por supuesto, describen un contrato con un único comprador y vendedor central, un contrato mucho más interesante permitiría un mercado donde cualquiera puede ofertar precios diferentes, o tal vez cargaría los precios directamente de una fuente externa.

Recarga automática

Cada vez que realiza una transacción en Pirl, debe pagar una tarifa al minero del bloque que calculará el resultado de su contrato inteligente. Si bien esto puede cambiar en el futuro, por el momento las tarifas solo se pueden pagar en Pirl y, por lo tanto, todos los usuarios de sus tokens lo necesitan. Los tokens en cuentas con un saldo menor que la tarifa se atascan hasta que el propietario pueda pagar la tarifa necesaria. Pero en algunos casos de uso, es posible que no desee que sus usuarios piensen en Pirl, blockchain o cómo obtener Pirl, por lo que un posible enfoque sería que su moneda reponga automáticamente el saldo del usuario tan pronto como detecte que el saldo es peligrosamente bajo.

Para hacer eso, primero debe crear una variable que contendrá la cantidad umbral y una función para cambiarla.

uint minBalanceForAccounts;

   función setMinBalance (uint minimumBalanceInFinney) onlyOwner {
        minBalanceForAccounts = minimumBalanceInFinney * 1 finney;
   }

 

Luego, agregue esta línea a la función de transferencia para que se reembolse al remitente:

  / * Enviar monedas * /
   transferencia de funciones (dirección _to, uint256 _valor) {
       …
       if (msg.sender.balance <minBalanceForAccounts)
           vender ((minBalanceForAccounts - msg.sender.balance) / sellPrice);
   }

 

También puede cambiarlo para que el remitente pague la tarifa al receptor:

  / * Enviar monedas * /
   transferencia de funciones (dirección _to, uint256 _valor) {
       …
       if (_to.balance            _to.send (sell ((minBalanceForAccounts - _to.balance) / sellPrice));
   }

Esto asegurará que ninguna cuenta que reciba el token tenga menos del Pirl necesario para pagar las tarifas.

Prueba de trabajo

Hay algunas formas de vincular su suministro de monedas a una fórmula matemática. Una de las formas más simples sería convertirlo en una "minería fusionada" con Pirl, lo que significa que cualquiera que encuentre un bloque en Pirl también obtendría una recompensa de su moneda, dado que cualquiera llama a la función de recompensa en ese bloque. Puede hacerlo utilizando la palabra clave especial coinbase que se refiere al minero que encuentra el bloque.

función giveBlockReward () {
       balanceOf [block.coinbase] + = 1;
   }

También es posible agregar una fórmula matemática, para que cualquiera que pueda hacer matemáticas pueda ganar una recompensa. En el siguiente ejemplo, debe calcular la raíz cúbica del desafío actual que obtiene un punto y el derecho para establecer el siguiente desafío:

uint currentChallenge = 1; // ¿Puedes averiguar la raíz cúbica de este número?

   function recompensaMathGeniuses (uint answerToCurrentReward, uint nextChallenge) {
       require (answerToCurrentReward ** 3 == currentChallenge); // Si la respuesta es incorrecta, no continúe
       balanceOf [msg.sender] + = 1; // Recompensa al jugador
       currentChallenge = nextChallenge; // Establecer el próximo desafío
   }

Por supuesto, si bien calcular las raíces cúbicas puede ser difícil para alguien hacerlas con la cabeza, son muy fáciles con una calculadora, por lo que una computadora puede romper fácilmente este juego. Además, dado que el último ganador puede elegir el próximo desafío, podrían elegir somPirling que conocen y, por lo tanto, no sería un juego muy justo para otros jugadores. Hay tareas que son fáciles para los humanos pero difíciles para las computadoras, pero generalmente son muy difíciles de codificar en scripts simples como estos. En cambio, un sistema más justo debería ser uno que sea muy difícil de hacer para una computadora, pero no es muy difícil de verificar para una computadora. Un gran candidato sería crear un desafío hash donde el retador tenga que generar hashes a partir de múltiples números hasta que encuentre uno que sea inferior a una dificultad dada.

Este proceso fue propuesto por primera vez por Adam Back en 1997 como Hashcash y luego fue implementado en Bitcoin por Satoshi Nakamoto como prueba de trabajo en 2008.

Si le gusta Hashing como una forma de emisión aleatoria de monedas, aún puede crear su propia moneda basada en Pirl que tiene una prueba de emisión de trabajo:

bytes32 public currentChallenge; // La moneda comienza con un desafío
   uint public timeOfLastProof; // Variable para realizar un seguimiento de cuándo se otorgaron las recompensas
   dificultad pública uint = 10 ** 32; // La dificultad comienza razonablemente baja

   función proofOfWork (uint nonce) {
       bytes8 n = bytes8 (sha3 (nonce, currentChallenge)); // Genera un hash aleatorio basado en la entrada
       require (n> = bytes8 (dificultad)); // Comprueba si está bajo la dificultad

       uint timeSinceLastProof = (ahora - timeOfLastProof); // Calcular el tiempo desde la última recompensa
       require (timeSinceLastProof> = 5 segundos); // Las recompensas no se pueden dar demasiado rápido
       balanceOf [msg.sender] + = timeSinceLastProof / 60 segundos; // La recompensa al ganador crece por minuto

       dificultad = dificultad * 10 minutos / tiempoSinceLastProof + 1; // Ajusta la dificultad

       timeOfLastProof = ahora; // Restablecer el contador
       currentChallenge = sha3 (nonce, currentChallenge, block.blockhash (block.number - 1)); // Guardar un hash que se usará como la próxima prueba
   }

También cambie la función Constructor (la que tiene el mismo nombre que el contrato, que se llama en la primera carga) para agregar esta línea, para que el ajuste de dificultad no se vuelva loco:

timeOfLastProof = ahora;

Una vez que el contrato esté en línea, seleccione la función "Prueba de trabajo", agregue su número favorito en el campo nonce e intente ejecutarlo. Si la ventana de confirmación muestra una advertencia roja que dice "No se pueden ejecutar los datos", regrese y elija otro número hasta que encuentre uno que permita que la transacción avance: este proceso es aleatorio. Si encuentra uno, se le otorgará 1 ficha por cada minuto que haya pasado desde que se le otorgó la última recompensa, y luego la dificultad del desafío se ajustará hacia arriba o hacia abajo para alcanzar un promedio de 10 minutos por recompensa.

Este proceso de tratar de encontrar el número que le dará una recompensa es lo que se llama minería: si la dificultad aumenta, puede ser muy difícil encontrar un número de la suerte, pero siempre será fácil verificar que haya encontrado uno.

Moneda mejorada

CODIGO COMPLETO
Si agrega todas las opciones avanzadas, así es como debería verse el código final:

solidez de pragma ^ 0.4.16;

propiedad del contrato {
   dirección del propietario público;

   función propiedad () pública {
       propietario = msg.sender;
   }

   modificador onlyOwner {
       require (msg.sender == propietario);
       _;
   }

   función transferOwnership (dirección newOwner) onlyOwner public {
       propietario = newOwner;
   }
}


solidez de pragma ^ 0.4.16;

propiedad del contrato {
   dirección del propietario público;

   función propiedad () pública {
       propietario = msg.sender;
   }

   modificador onlyOwner {
       require (msg.sender == propietario);
       _;
   }

   función transferOwnership (dirección newOwner) onlyOwner public {
       propietario = newOwner;
   }
}

interface tokenRecipient {function acceptApproval (address _from, uint256 _value, address _token, bytes _extraData) public; }

TokenERC20 de contrato {
   // Variables públicas del token
   cadena de nombre público;
   símbolo público de cadena;
   uint8 decimales públicos = 18;
   // 18 decimales es el valor predeterminado altamente sugerido, evite cambiarlo
   uint256 public totalSupply;

   // Esto crea una matriz con todos los saldos
   mapping (address => uint256) public balanceOf;
   asignación (dirección => asignación (dirección => uint256)) asignación pública;

   // Esto genera un evento público en la cadena de bloques que notificará a los clientes
   Transferencia de eventos (dirección indexada desde, dirección indexada a, valor uint256);

   // Esto notifica a los clientes sobre la cantidad quemada
   evento Burn (dirección indexada desde, valor uint256);

   /**
    * Función Constrctor
    *
    * Inicializa el contrato con tokens de suministro iniciales para el creador del contrato
    */
   función TokenERC20 (
       uint256 initialSupply,
       string tokenName,
       token de cadena
   ) público {
       totalSupply = initialSupply * 10 ** uint256 (decimales); // Actualizar el suministro total con la cantidad decimal
       balanceOf [msg.sender] = totalSupply; // Dale al creador todos los tokens iniciales
       nombre = tokenName; // Establecer el nombre para mostrar
       símbolo = tokenSymbol; // Establecer el símbolo para mostrar
   }

   /**
    * Transferencia interna, solo puede ser convocada por este contrato
    */
   función _transfer (dirección _de, dirección _to, uint _valor) interna {
       // Evita la transferencia a la dirección 0x0. Use burn () en su lugar
       require (_to! = 0x0);
       // Comprueba si el remitente tiene suficiente
       require (balanceOf [_from]> = _value);
       // Verificar desbordamientos
       require (balanceOf [_to] + _value> balanceOf [_to]);
       // Guardar esto para una afirmación en el futuro
       uint previousBalances = balanceOf [_from] + balanceOf [_to];
       // Restar del remitente
       balanceOf [_from] - = _value;
       // Añadir lo mismo al destinatario
       balanceOf [_to] + = _value;
       Transferencia (_from, _to, _value);
       // Las afirmaciones se usan para usar análisis estático para encontrar errores en su código. Nunca deberían fallar
       afirmar (balanceOf [_from] + balanceOf [_to] == anterioresBalances);
   }

   /**
    * Transferir tokens
    *
    * Envíe tokens `_value` a` _to` desde su cuenta
    *
    * @param _to La dirección del destinatario
    * @param _value la cantidad a enviar
    */
   transferencia de funciones (dirección _to, uint256 _valor) público {
       _transfer (msg.sender, _to, _value);
   }

   /**
    * Transferir tokens desde otra dirección
    *
    * Enviar tokens `_value` a` _to` en nombre de `_from`
    *
    * @param _from La dirección del remitente
    * @param _to La dirección del destinatario
    * @param _value la cantidad a enviar
    */
   función transferFrom (address _from, address _to, uint256 _value) devoluciones públicas (éxito de bool) {
       require (_value <= asignación [_from] [msg.sender]); // Verificar la asignación
       asignación [_from] [msg.sender] - = _value;
       _transfer (_from, _to, _value);
       volver verdadero;
   }

   /**
    * Establecer asignación para otra dirección
    *
    * Permite a `_spender` gastar no más de` _value` tokens en tu nombre
    *
    * @param _spender La dirección autorizada para gastar
    * @param _value la cantidad máxima que pueden gastar
    */
   función aprobar (dirección _spender, uint256 _value) public
       retornos (éxito de bool) {
       asignación [msg.sender] [_ spender] = _value;
       volver verdadero;
   }

   /**
    * Establecer asignación para otra dirección y notificar
    *
    * Permite que `_spender` no gaste más de` _value` tokens en su nombre, y luego haga ping al contrato al respecto
    *
    * @param _spender La dirección autorizada para gastar
    * @param _value la cantidad máxima que pueden gastar
    * @param _extraData alguna información adicional para enviar al contrato aprobado
    */
   función approveAndCall (dirección _spender, uint256 _value, bytes _extraData)
       público
       retornos (éxito de bool) {
       tokenRecipient spender = tokenRecipient (_spender);
       if (aprobar (_spender, _value)) {
           spender.receiveApproval (msg.sender, _value, this, _extraData);
           volver verdadero;
       }
   }

   /**
    * Destruye fichas
    *
    * Eliminar los tokens `_value` del sistema de forma irreversible
    *
    * @param _value la cantidad de dinero para quemar
    */
   función burn (uint256 _value) retornos públicos (éxito de bool) {
       require (balanceOf [msg.sender]> = _value); // Comprueba si el remitente tiene suficiente
       balanceOf [msg.sender] - = _value; // Restar del remitente
       totalSupply - = _value; // Actualizaciones totalSupply
       Grabar (msg.sender, _value);
       volver verdadero;
   }

   /**
    * Destruye tokens de otra cuenta
    *
    * Elimine los tokens `_value` del sistema de forma irreversible en nombre de` _from`.
    *
    * @param _de la dirección del remitente
    * @param _value la cantidad de dinero para quemar
    */
   función burnFrom (dirección _from, uint256 _value) devoluciones públicas (éxito de bool) {
       require (balanceOf [_from]> = _value); // Comprueba si el saldo objetivo es suficiente
       require (_value <= asignación [_from] [msg.sender]); // Verificar la asignación
       balanceOf [_from] - = _value; // Restar del saldo objetivo
       asignación [_from] [msg.sender] - = _value; // Restar de la asignación del remitente
       totalSupply - = _value; // Actualizar totalSupply
       Grabar (_de, _valor);
       volver verdadero;
   }
}

/******************************************/
/ * TOKEN AVANZADO COMIENZA AQUÍ * /
/******************************************/

contrato MyAdvancedToken es propiedad, TokenERC20 {

   uint256 precio de venta público;
   uint256 public buyPrice;

   mapping (address => bool) public frozenAccount;

   / * Esto genera un evento público en la cadena de bloques que notificará a los clientes * /
   evento FrozenFunds (dirección de destino, bool frozen);

   / * Inicializa el contrato con tokens de suministro iniciales para el creador del contrato * /
   función MyAdvancedToken (
       uint256 initialSupply,
       string tokenName,
       token de cadena
   ) TokenERC20 (initialSupply, tokenName, tokenSymbol) public {}

   / * Transferencia interna, solo se puede llamar mediante este contrato * /
   función _transfer (dirección _de, dirección _to, uint _valor) interna {
       require (_to! = 0x0); // Evita la transferencia a la dirección 0x0. Use burn () en su lugar
       require (balanceOf [_from]> = _value); // Comprueba si el remitente tiene suficiente
       require (balanceOf [_to] + _value> balanceOf [_to]); // Verificar desbordamientos
       require (! frozenAccount [_from]); // Comprueba si el remitente está congelado
       require (! frozenAccount [_to]); // Comprobar si el destinatario está congelado
       balanceOf [_from] - = _value; // Restar del remitente
       balanceOf [_to] + = _value; // Añadir lo mismo al destinatario
       Transferencia (_from, _to, _value);
   }

   /// @notice Crear tokens `mintedAmount` y enviarlo a` target`
   /// @param Dirección de destino para recibir los tokens
   /// @param mintedAmount la cantidad de tokens que recibirá
   función mintToken (objetivo de dirección, uint256 mintedAmount) onlyOwner public {
       balanceOf [target] + = mintedAmount;
       totalSupply + = mintedAmount;
       Transferencia (0, this, mintedAmount);
       Transferencia (this, target, mintedAmount);
   }

   /// @notice `congelar? Prevenir | Permitir que `` target '' envíe y reciba tokens
   /// @param Dirección de destino a congelar
   /// @param freeze para congelarlo o no
   función freezeAccount (dirección de destino, bool freeze) solamente Propietario público {
       frozenAccount [target] = congelar;
       FrozenFunds (objetivo, congelación);
   }

   /// @notice Permitir a los usuarios comprar tokens para `newBuyPrice` Pirl y vender tokens para` newSellPrice` Pirl
   /// @param newSellPrice Precio que los usuarios pueden vender al contrato
   /// @param newBuyPrice Precio que los usuarios pueden comprar del contrato
   función setPrices (uint256 newSellPrice, uint256 newBuyPrice) onlyOwner public {
       sellPrice = newSellPrice;
       buyPrice = newBuyPrice;
   }

   /// @notice Compre tokens del contrato enviando Pirl
   función buy () pagadera pública {
       uint cantidad = msg.value / buyPrice; // calcula la cantidad
       _transfer (esto, msg.sender, cantidad); // hace las transferencias
   }

   /// @notice Vender tokens de `cantidad` para contratar
   /// @param cantidad de tokens que se venderán
   función vender (cantidad uint256) pública {
       require (this.balance> = cantidad * precio de venta); // comprueba si el contrato tiene suficiente Pirl para comprar
       _transfer (msg.sender, esto, cantidad); // hace las transferencias
       msg.sender.transfer (cantidad * precio de venta); // envía Pirl al vendedor. Es importante hacer esto al final para evitar ataques de recurrencia
   }
}

Desplegando

Desplácese hacia abajo y verá un costo estimado para la implementación. Si lo desea, puede cambiar el control deslizante para establecer una tarifa más pequeña, pero si el precio está muy por debajo de la tasa promedio del mercado, su transacción podría demorar más en recuperarse. Haga clic en Implementar y escriba su contraseña. Después de unos segundos, se lo redirigirá al panel de control y, en Últimas transacciones, verá una línea que dice "creación de contrato". Espere unos segundos a que alguien elija su transacción y luego verá un rectángulo azul lento que representa cuántos otros nodos han visto su transacción y los han confirmado. Cuantas más confirmaciones tenga, más seguridad tendrá de que su código ha sido implementado.

Haga clic en el enlace que dice la página Administrador y se lo llevará al panel de control del banco central más simple del mundo, donde puede hacer lo que quiera con su moneda recién creada.

En el lado izquierdo, debajo de Leer del contrato, tiene todas las opciones y funciones que puede usar para leer información del contrato, de forma gratuita. Si su token tiene un propietario, mostrará su dirección aquí. Copie esa dirección y péguela en Saldo de y le mostrará el saldo de cualquier cuenta (el saldo también se muestra automáticamente en cualquier página de cuenta que tenga tokens).

En el lado derecho, debajo de Escribir en el contrato, verá todas las funciones que puede usar para alterar o cambiar la cadena de bloques de cualquier manera. Estos costarán gasolina. Si creó un contrato que le permite acuñar nuevas monedas, debe tener una función llamada "Mint Token". Seleccionarlo

Seleccione la dirección donde se crearán esas nuevas monedas y luego la cantidad (si tiene decimales establecidos en 2, luego agregue 2 ceros después de la cantidad, para crear la cantidad correcta). En Ejecutar, seleccione la cuenta que estableció como propietario, deje el monto de Pirl en cero y luego presione ejecutar.

Después de algunas confirmaciones, el saldo del destinatario se actualizará para reflejar la nueva cantidad. Pero es posible que su billetera receptora no lo muestre automáticamente: para conocer los tokens personalizados, la billetera debe agregarlos manualmente a una lista de observación. Copie su dirección de token (en la página de administración, presione copiar dirección) y envíela a su destinatario. Si aún no lo han hecho, deben ir a la pestaña de contratos, presionar Watch Token y luego agregar la dirección allí. El usuario final puede personalizar el nombre, los símbolos y las cantidades decimales que se muestran, especialmente si tienen otros tokens con un nombre similar (o el mismo). El ícono principal no se puede cambiar y los usuarios deben prestarles atención al enviar y recibir tokens para asegurarse de que están tratando con el trato real y no con algún token de imitación.

Usando tu moneda

Una vez que haya implementado sus tokens, se agregarán a su lista de tokens observados, y el saldo total se mostrará en su cuenta. Para enviar tokens, solo vaya a la pestaña Enviar y seleccione una cuenta que contenga tokens. Los tokens que tiene la cuenta aparecerán justo debajo de Pirl. Selecciónelos y luego escriba la cantidad de tokens que desea enviar.

Si desea agregar el token de otra persona, simplemente vaya a la pestaña Contratos y haga clic en Ver token. Por ejemplo, para agregar el token Pirl Vortex a su lista de observación, simplemente agregue la dirección 0x0489A975393A1cD0330740040141D702C35180cb

¿Ahora que?

Acabas de aprender cómo puedes usar Pirl para emitir un token, que puede representar lo que quieras. Pero, ¿qué puedes hacer con las fichas? Puede usar, por ejemplo, los tokens para representar una participación en una empresa o puede usar un comité central para votar cuándo emitir nuevas monedas para controlar la inflación. También puede usarlos para recaudar dinero para una causa, a través de una venta masiva. ¿Qué vas a construir a continuación?

Material de referencia: ethereum.org


Suscríbete a nuestro boletín

Recibe actualizaciones y aprende de los mejores

Más para explorar

Pirl y Vseed revolucionan el negocio médico

Pirl y Vseed revolucionan el negocio médico Pirl se complace en anunciar una asociación a largo plazo con la empresa conjunta Vseed. Vseed usará el completo

es_ESEspañol
en_USEnglish fr_FRFrançais nl_NLNederlands tr_TRTürkçe pt_PTPortuguês ru_RUРусский ko_KR한국어 zh_CN简体中文 hi_INहिन्दी es_ESEspañol