Tutorial – Crear Subastas en Ethereum

Hola a todos!

En esta oportunidad aprenderemos a crear subastas en Ethereum. Si bien es las ultra conversado en la red, me gustaría poder explicar las partes que componen a este Contrato Inteligente, de modo que luego en la siguiente entrada podamos subir un poco el nivel crear una fábrica de subasta. En ese tutorial aprenderemos un poco sobre las interfaces, ya que las requerimos para crear un Contrato Inteligente desde otro Contrato Inteligente.

En esta oportunidad veremos un poco sobre lo que es la última versión de Solidity y como nos cambia las reglas del juego con la declaración de variables tipo address que deseamos que puedan recibir dinero.

¡Vamos a crear una subasta en ethereum!

Para comenzar nuestro Contrato Inteligente para subasta, recolectemos algo de información primero: Sabemos que una subasta consiste en ofrecer un producto X con un precio inicial y que las personas interesadas puedan ir mejorando este precio, obviamente con una fecha límite. Sabiendo esto, podemos empezar a pensar en cuales son las funciones y variables que requeriremos.

Variables

  1. Un subastador que recibirá el dinero al final de la subasta: Variable tipo address payable, ya que luego de finalizada la subasta debe recibir el dinero recaudado.
  2. Un precio inicial (monto) que se debe mejorar: Variable tipo uint, ya que el dinero no puede ser negativo.
  3. Una fecha límite: Variable tipo uint, ya que el tiempo lo trataremos como “marca de tiempo” (timestamp) y tampoco puede ser un valor negativo.
  4. Un ganador: Variable tipo address payable, ya que si mejoran su oferta debe recibir el dinero de vuelta.
  5. Estado del contrato: Variable tipo bool con valor true por defecto

Nota: En la versión 0.5.6 de Solidity si no añadimos la palabra reservada payable a las variables tipo address, entonces no podremos invocar el método que nos permite depositar dinero (transfer)

Sabiendo lo anterior, podemos escribir ya un poco de código, sin “lógica de negocio” todavía:

pragma solidity ^0.5.6;

/**
 * Autor: Javier Guajardo
 * Website: https://ethereumchile.cl
 * Twitter: @ethereumchile
**/

contract Subasta {
    
    address payable public subastador;
    address payable public ganador;
    bool public recibido;
    uint public monto = 0.1 ether;
    uint public fecha_limite = now + 2 hours;
    bool public activa = true;    
    constructor() public {
        subastador = msg.sender;
    }
    
}

Nota 1: El constructor nos ayuda a determinar que el subastador es quien publica el contrato (msg.sender)

Nota 2: La fecha límite es 2 horas desde la publicación del contrato. (now + 2 hours)


Funciones

  1. Ofertar (payable): Permite a los participantes depositar dinero en la subasta. Maneja la lógica de la subasta, ya que es la encargada de evaluar: Si la subasta sigue activa, que la oferta enviada sea mejor que la última oferta recibida y que el oferente sea distinto al último (ya que no tiene sentido mejorar nuestra propia oferta y pagar de más voluntariamente).
  2. Retirar: Le permitirá al subastador recibir el dinero cuando haya finalizado la subasta.

Nota: La función Ofertar debe ser pública y llevar la palabra reservada payable, que la habilita para recibir dinero.

El código se va viendo más o menos así:

pragma solidity ^0.5;

/**
 * Autor: Javier Guajardo
 * Website: https://ethereumchile.cl
 * Twitter: @ethereumchile
**/

contract Subasta {
    
    address payable public subastador;
    address payable public ganador;
    uint public monto = 0.1 ether;
    uint public fecha_limite = now + 2 hours;
    bool public activa = true;
    
    constructor() public {
        subastador = msg.sender;
    }
    
    function ofertar() public payable {

    }
    
    function retirar() public {
        
    }
    
}

Planeando y Programando la función Ofertar en la subasta

Primero: Cuando alguien realiza una oferta tenemos que verificar que la fecha en la que se envía la transacción sea inferior a la fecha límite de la subasta.

Segundo: Si pasamos la verificación anterior entonces tenemos que comprobar que el monto enviado sea mayor al último monto recibido, ya que la gracia de la subasta es que siempre se esté mejorando el precio.

Tercero: Si pasamos ambas verificaciones, entonces podemos actualizar las variables que determinan quién es el mejor oferente y cuánto es lo que está ofreciendo. Almacenar estos últimos campos nos ayudará para devolver el dinero en caso de que alguien mejore su oferta.

Para realizar las comprobaciones mencionadas anteriormente utilizaremos la función interna de Solidity llamada require(); que recibe como parámetro una condición, que de resultar false entonces revierte la transacción y devuelve el dinero enviado, De ser true entonces continúa con las instrucciones de la función, por lo que es lógico añadir el require(); al comienzo de la función, así no modificamos el estado del contrato en caso de que algo no se cumpla.

Nuestro require(); recibirá como parámetro dos condiciones concatenadas con el operador and (&&):

  1. fecha_limite > now. La fecha límite tiene que estar en el futuro, por lo que es lógico ayudarnos de la palabra reservada now, que en Solidity significa “la fecha actual” (en timestamp).
  2. msg.value > monto. El valor enviado (msg.value) a la función Ofertar tiene que ser mayor a la última oferta (monto).

Esto en lenguaje Solidity quedaría así:

Si el valor enviado (msg.value) es mayor al monto anterior, entonces debemos devolver el dinero (monto) al oferente anterior (ganador) y posteriormente actualizar esta información, haciendo saber al Smart Contract que hay un nuevo monto y un nuevo ganador, de modo que en la siguiente mejor oferta el Smart Contract pueda nuevamente saber cuanto devolver al último oferente.

Para devolver el dinero al último oferente entonces debemos utilizar el método transfer asociado al tipo de dato address en Solidity. El método transfer recibe como parámetro el valor a enviar, expresado en WEI.

Luego de enviado el dinero al ganador anterior, debemos actualizar quien es el nuevo ganador (msg.sender) y cuanto dinero envió (msg.value). Nuestra función Ofertar entonces queda así:

function ofertar() public payable {
    require(msg.value > monto && fecha_limite > now);
    ganador.transfer(monto);
    monto = msg.value;
    ganador = msg.sender;
}


Planeando y Programando la función Retirar

¡Estamos muy cerca de terminar nuestro Contrato Inteligente para subastas! No te rindas!!

Primero: Solo el subastador puede retirar el dinero recaudado en la subasta. (msg.sender == subastador)

Segundo: El subastador puede retirar el dinero de la subasta solo cuando ésta haya terminado. (now > fecha_limite)

Tercero: La subasta debe estar activa, así evitamos gastar GAS en una transacción que no hará nada luego de cobrado el dinero.

Si pasamos ambas verificaciones entonces podemos invocar el método transfer utilizando la variable tipo address subastador, al que le enviaremos como parámetro la mejor oferta recibida (monto) y finalmente diremos que la subasta pasó de estar activa (true) a estar inactiva (false).

require(msg.value > monto && fecha_limite > now && activa); 

Si pasa esta verificación entonces hacemos la transferencia al subastador por el mejor monto enviado.

function retirar() public {
    require(msg.sender ==subastador && now > fecha_limite && activa);
    ganador.transfer(monto);
    activa = false;
}

El código terminado luce así:

pragma solidity ^0.5.6;

/**
 * Autor: Javier Guajardo
 * Website: https://ethereumchile.cl
 * Twitter: @ethereumchile
**/

contract Subasta {
    
    address payable public subastador;
    address payable public ganador;
    uint public monto = 0.1 ether;
    uint public fecha_limite = now + 2 hours;
    bool public activa = true;
    
    constructor() public {
        subastador = msg.sender;
    }
    
    function ofertar() public payable {
        require(msg.value > monto && fecha_limite > now);
        ganador.transfer(monto);
        monto = msg.value;
        ganador = msg.sender;
    }
    
    function retirar() public {
        require(msg.sender ==subastador && now > fecha_limite && activa);
        ganador.transfer(monto);
        activa = false;
    }
    
}

Para compilar nuestro Smart Contract te aconsejo seguir el siguiente tutorial: [dApp] dMessenger – Chat / Libro de visitas descentralizado y si no quieren utilizar Rinkeby, pueden usar Kovan o crear su propia blockchain privada

Ahora ya sabes como crear subastas en Ethereum. ¿Tienes dudas? Déjalas en los comentarios 🙂

Saludos, Javier Guajardo.

También te podría gustar...

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *