Controla el tiempo en la Blockchain


Al desarrollar smart contracts, entender el concepto del tiempo es muy importante. ¿Cómo puedes asegurarte de que un contrato ejecute acciones a partir de momentos específicos? La clave está en block.timestamp.

En este artículo, te explicamos cómo utilizar esta variable, exploramos sus aplicaciones en contratos inteligentes y consejos para realizar pruebas efectivas que garanticen su correcto funcionamiento.

¿Qué es block.timestamp?

block.timestamp es una variable global en Solidity que devuelve la marca de tiempo (timestamp) del bloque actual en segundos desde la época UNIX (1 de enero de 1970). Esta variable se utiliza comúnmente para hacer referencia al momento en que se mina un bloque en la blockchain.

Usos comunes de block.timestamp

En los contratos inteligentes, block.timestamp se utiliza en una variedad de escenarios:

  • Restricciones de tiempo: Para establecer límites de tiempo en funciones, como permitir que una función se ejecute solo después de una fecha específica.
  • Cálculo de duraciones: Para calcular cuánto tiempo ha pasado desde un evento anterior, por ejemplo, para calcular intereses en una aplicación DeFi.
pragma solidity ^0.8.0;

contract Subasta {
 uint public tiempoFinal;
 address public mejorPujador;
 uint public mejorPuja;
 event PujaHecha();


 constructor(uint _duracion) {
   tiempoFinal = block.timestamp + _duracion;
 }

 function pujar() public payable {
   require(block.timestamp < tiempoFinal, "La subasta ha terminado");
   require(msg.value > mejorPuja, "Puja no es lo suficientemente alta");

   mejorPujador = msg.sender;
   mejorPuja = msg.value;

   emit PujaHecha();
 }
} 

En este ejemplo, block.timestamp se utiliza para establecer el tiempo de finalización de una subasta y para verificar si una puja se realiza dentro del período permitido.

Unidades de tiempo

Puedes utilizar diversas unidades de tiempo para hacer que el código sea más legible y fácil de entender. Las unidades de tiempo aceptadas son: seconds, minutes, hours, days, weeks.

Estas expresiones se convierten automáticamente en sus valores respectivos en segundos. Por ejemplo, si quieres definir una semana en tu contrato, puedes hacerlo de la siguiente manera:

uint public constant week = 1 weeks;

En este caso, 1 weeks se convierte en 604800, que es la cantidad de segundos en una semana.

Consideraciones de seguridad

Aunque block.timestamp es una variable muy utilizada, tiene algunos riesgos.

Los mineros tienen un pequeño grado de control sobre esta variable, ya que pueden manipularla ligeramente (por algunos segundos) el tiempo en el que es minado el bloque.

Para mitigar estos riesgos, es recomendable:

  • Ser consciente de su imprecisión: Los ajustes menores en block.timestamp pueden afectar contratos que dependen de tiempos precisos, por lo que es importante considerar este factor al diseñar y validar la lógica temporal.
  • No usar block.timestamp para generar números aleatorios: Dado que su valor puede ser manipulado, no es adecuado para funciones que requieren aleatoriedad criptográfica o decisiones basadas en números aleatorios.

Cómo testear el block.timestamp

Para probar contratos inteligentes que utilizan block.timestamp, puedes utilizar frameworks como Hardhat o Truffle. Estos frameworks permiten manipular y avanzar en el tiempo dentro del entorno de pruebas para simular diferentes escenarios.

Aquí hay un ejemplo de cómo podrías probar un contrato que depende de block.timestamp usando Hardhat:

import { ethers } from 'hardhat'
import { time } from "@nomicfoundation/hardhat-network-helpers"
import { expect } from 'chai'
import { type Subasta } from '@contracts/Subasta'

describe("Subasta", function () {
 let subasta: Subasta

 this.beforeEach(async () => {
  subasta = await (await ethers.getContractFactory("Subasta")).deploy(60)
  await subasta.waitForDeployment()
 })

 it("Debe permitir pujar antes de que termine el tiempo", async () =>{
  await time.increase(30)// Avanzar 30 segundos en la blockchain

  await expect(subasta.pujar({ value: ethers.parseEther("1.0") }))
   .to.emit(subasta, "PujaHecha")
 })

 it("Debe rechazar pujas después de que termine el tiempo", async () =>{
  await time.increase(70)// Avanzar 70 segundos en la blockchain

  await expect(subasta.pujar({ value: ethers.parseEther("1.0") }))
   .to.be.revertedWith("La subasta ha terminado")
 })
})

Así que ya lo sabes, ¡controlar el tiempo en la blockchain es casi como tener un DeLorean! Recuerda, mientras los contratos inteligentes se encargan de manejar el tiempo con precisión, tú puedes relajarte y disfrutar de un café sin preocuparte por el “Back to the Future” en tu código.