El pasado lunes tuve la oportunidad de impartir un pequeño taller en mi empresa sobre los smart contracts escritos en Solidity. La idea principal era evangelizar a mis compañeros sobre este tipo de programas, ver cómo se desarrollan y sobre todo como se despliegan y ejecutan dentro de la red Ethereum.

En una empresa de TI estamos acostumbrados a poner en producción multitud de productos y servicios pero no cabe duda que los smart contracts no son programas como a los que estamos acostumbrados. Baste decir que un programa de este tipo no se puede modificar una vez desplegado, cosa que choca bastante en un primer momento.

El caso es que únicamente tenía hora y media para contarles a mis compañeros, un grupo bastante heterogéneo, que era eso del smart contract y además hacer algo práctico para que pudieran jugar un poco, por eso lo de “techplay” 😉

Afortunadamente, para el desarrollo de smart contracts en Solidity, al menos para jugar un poco, tenemos la herramienta Remix que sólo necesita un navegador para desarrollar y probar un contrato. Así que si quieres empezar a trastear ni siquiera necesitas instalar nada.

¿Qué ejemplo podría hacer suficientemente interesante pero que pudiéramos hacer en tan poco tiempo? pues bien, lo que se me ocurrió es hacer un contrato para vender “espacio publicitario” en la tapa de mi portátil… ¿por qué poner pegatinas sin ninguna contrapartida? el que quiera que ponga su pegatina que lo pague ¿no? 🙂

Así fue como empecé a darle forma a un pequeño programa que debería tener al menos estas características:

  • Un literal con la información acerca del contrato
  • Que se pudiera especificar quién es el dueño
  • Que se pueda transferir la propiedad del contrato con la condición de que sólo el dueño en ese momento puede hacer esta transferencia
  • Poner un anuncio, en principio de manera gratuita
  • Cobrar por poner un anuncio
  • Transferir el dinero recaudado (el Ether) a la cuenta del dueño del contrato

Y lo mejor de todo esto, es que muchas de características están incluidas en el propio lenguaje, así que podemos tener todo esto en un programa muy pequeño, cosa impensable en otros lenguajes como Java.

Un literal con la información acerca del contrato

Lo primero que tenemos que tener aquí es una variable de tipo string pública con la información o descripción acerca de esta instancia del contrato. Para ello además usaremos un constructor. Sería algo así:

pragma solidity >=0.4.22 <0.6.0;

contract LaptopAds{

    //informacion sobre este espacio publicitario

    string public information;

    //inicializa el contrato al desplegarlo

    constructor(string memory info) public{

        information = info;

    }

}

Especificando el dueño del contrato

Cada usuario de la red Ethereum está identificada una sería de bytes que constituyen una dirección. Los contratos también están identificada con una, aunque empiezan por otro prefijo.

Para guardar una variable del contrato utilizaremos por lo tanto el tipo address y para asegurarnos que sólo el dueño del contrato pueda especificar esta variable la asignaremos en el constructor (el constructor se invoca sólamente en el despliegue del contrato y por tanto es el dueño el que lo invoca).

Afortunadamente el lenguaje Solidity tiene una variable incorporada que representa el mensaje (la llamada al método) y dentro de él, el campo sender que es el usuario o contrato que ha invocado a este método. Por lo tanto únicamente hay que guardar el valor de msg.sender en la variable owner que hemos creado en el programa.

pragma solidity >=0.4.22 <0.6.0;

contract LaptopAds{

    //informacion sobre este espacio publicitario

    string public information;

    //owner del portatil

    address public owner;

    //inicializa el contrato al desplegarlo

    constructor(string memory info) public{

        information = info;

        owner = msg.sender;

    }

}

Añadir transferencia de propiedad

Para añadir el cambio de dueño, vamos a añadir una función que cambie el valor de la propiedad owner del contrato. Esta función tiene un parámetro de entrada que tiene el valor de la dirección del nuevo dueño.

    function transferOwnership(address newOwner) public onlyOwner{

        require(newOwner != address(0));

        owner = newOwner;

    }

Como medida de seguridad se comprueba que la dirección del nuevo dueño no es la dirección “nula” o cero.

Si os fijáis en la función, podéis ver que cualquier puede llamarla. Es decir, cualquiera puede establecerse como nuevo dueño del contrato. Necesitamos que se compruebe que sólo el dueño actual pueda cambiar la propiedad del contrato.

Comprobación que sólo el dueño pueda cambiar la propiedad

Para hacer esto tenemos que añadir un modifier, es decir, un elemento del lenguaje que actúa a modo de “macro” y que se puede aplicar a otras funciones.

Este sería el macro:

    modifier onlyOwner() {

        require(msg.sender == owner);

        _;

    }

Con esto, sólo queda modificar la función para añadirle onlyOwner al final de la misma:

  function transferOwnership(address newOwner) public onlyOwner{

        require(newOwner != address(0));

        owner = newOwner;

    }

En el modificador o marco, la línea “_;” viene a decir que todo el código de la función se ejecute ahí. Es decir, es como si copio todo el código de la función transferOwnership y lo pego sustituyendo “_;”. Es un poco raro, pero recuerda a las macros que hay en C.

Poner un anuncio

Por fin, vamos a añadir el método de poner anuncio en el contrato, al fin y al cabo, es su propósito 😉

Para ello simplemente vamos a definir una variable que contendrá la cadena de texto con el anuncio, la propiedad ad.

Además nos aseguramos que sólo el dueño pueda llamarlo.

pragma solidity >=0.4.22 <0.6.0;

contract LaptopAds{

    //informacion sobre este espacio publicitario

    string public information;

    //owner del portatil

    address public owner;

    //texto del anuncio

    string public ad;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    //inicializa el contrato al desplegarlo

    constructor(string memory info) public{

        information = info;

        owner = msg.sender;

    }

    function transferOwnership(address newOwner) public onlyOwner{

        require(newOwner != address(0));

        emit OwnershipTransferred(owner, newOwner);

        owner = newOwner;

    }

    modifier onlyOwner() {

        require(msg.sender == owner);

        _;

    }

    //funcion de negocio

    //hay que pagar el contrato

    function setAdd(string memory newAdd) public{

        ad = newAdd;

    }

}

Cobrar por poner un anuncio

Nos falta sin embargo, poder cobrar por poner un anuncio. Esto realmente es la parte más fácil, gracias a que viene incluido en el lenguaje solidity.

Simplemente tenemos que añadir la palabra reservada payable en la definición de la función:

    function setAdd(string memory newAdd) public payable{

        require(msg.value >= 0.01 ether);

        ad = newAdd;

    }

Además, gracias a la sentencia require, nos aseguramos una cantidad mínima que el cliente tiene que pagar por poner el anuncio, en este caso, un céntimo de ether.

De esta manera, el cliente, cuando quiera poner un anuncio, tendrá que llamar a esta función y asignar en la llamada al menos un céntimo de sus fondos de la moneda de Ethereum.

Este dinero se va guardando en una variable especial llamada balance del smart contract que funciona como un monedero. ¿Y como sabemos las monedas que lleva recaudadas el contrato? gracias al atributo balance

    function getBalance() public view returns (uint256){

        return address(this).balance;

    }

Interesante ¿verdad?. Puesto esto es sólo la punta del iceberg. Espero que os haya picado el gusanillo de saber más de smart contracts con Solidity 😉