Ir al contenido principal

Cómo simular contratos inteligentes de Solidity para probarlos

Solidity
contratos Inteligentes
pruebas
simular
Intermedio
Markus Waas
2 de mayo de 2020
4 minuto leído

Los objetos simuladosopens in a new tab son un patrón de diseño común en la programación orientada a objetos. Viene de la antigua palabra francesa «mocquer» con el significado de «reírse de algo» y evolucionó a «imitar a algo real» que es, en realidad, lo que hacemos en programación. Por tanto, ríase todo lo que quiera de sus contratos inteligentes si quieres, pero simúlelos siempre que pueda. ¡Le simplifica la vida!

Pruebas unitarias de contratos con simulaciones

Simular un contrato significa básicamente crear una segunda versión del contrato que se comporta de manera muy similar al original, pero de una forma que el desarrollador puede controlar fácilmente. A menudo acaba con contratos complejos donde solo desea probar unidades pequeñas del contrato. El problema es: ¿qué sucedería si esta pequeña parte requiere un estado de contrato muy específico que es complicado para comenzar?

Puede escribir una lógica de configuración de prueba compleja cada vez que el contrato se encuentre en el estado requerido, o escriba una simulación. Simular un contrato es fácil con herencia. Simplemente crea un segundo contrato simulado que hereda del original. Ahora puede sobrescribir funciones a su imitación. Veámoslo mejor poniendo un ejemplo.

Ejemplo: ERC20 privado

Usamos el ejemplo de un contrato ERC-20 que tiene un tiempo inicial privado. El propietario puede administrar usuarios privados y solo ellos estarán autorizados a recibir tókenes al principio. Una vez transcurrido un periodo específico, cualquiera podrá usar los tókenes. Si tiene curiosidad, estamos usando el hook _beforeTokenTransferopens in a new tab de los nuevos contratos OpenZeppelin v3.

1pragma solidity ^0.6.0;
2
3import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
4import "@openzeppelin/contracts/access/Ownable.sol";
5
6contract PrivateERC20 is ERC20, Ownable {
7 mapping (address => bool) public isPrivateUser;
8 uint256 private publicAfterTime;
9
10 constructor(uint256 privateERC20timeInSec) ERC20("PrivateERC20", "PRIV") public {
11 publicAfterTime = now + privateERC20timeInSec;
12 }
13
14 function addUser(address user) external onlyOwner {
15 isPrivateUser[user] = true;
16 }
17
18 function isPublic() public view returns (bool) {
19 return now >= publicAfterTime;
20 }
21
22 function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
23 super._beforeTokenTransfer(from, to, amount);
24
25 require(_validRecipient(to), "PrivateERC20: invalid recipient");
26 }
27
28 function _validRecipient(address to) private view returns (bool) {
29 if (isPublic()) {
30 return true;
31 }
32
33 return isPrivateUser[to];
34 }
35}
Mostrar todo

Y ahora vamos a simularlo.

1pragma solidity ^0.6.0;
2import "../PrivateERC20.sol";
3
4contract PrivateERC20Mock is PrivateERC20 {
5 bool isPublicConfig;
6
7 constructor() public PrivateERC20(0) {}
8
9 function setIsPublic(bool isPublic) external {
10 isPublicConfig = isPublic;
11 }
12
13 function isPublic() public view returns (bool) {
14 return isPublicConfig;
15 }
16}
Mostrar todo

Recibirás uno de los siguientes mensajes de error:

  • PrivateERC20Mock.sol: TypeError: Overriding function is missing "override" specifier.
  • PrivateERC20.sol: TypeError: Trying to override non-virtual function. ¿Olvidó añadir "virtual"?.

Como estamos usando la nueva versión 0.6 de Solidity, debemos añadir la palabra clave virtual a las funciones que pueden ser sobrescritas y override en la función que sobrescribe. Así que añadamos esos modificadores a ambas funciones isPublic.

Ahora en sus pruebas unitarias, puede usar PrivateERC20Mock en su lugar. Cuando desee probar el comportamiento durante el tiempo de uso privado, use setIsPublic(false) y de igual manera setIsPublic(true) para probar el tiempo de uso público. Por supuesto, en nuestro ejemplo, también podríamos usar time helpersopens in a new tab para ajustar los tiempos según corresponda. Esperamos que la idea de simular le haya quedado ahora clara y puede imaginar situaciones en las que todo no es tan sencillo como simplemente hacer avanzar el tiempo.

Simulando muchos contratos

Puede volverse un tanto caótico si tiene que crear otro contrato para cada imitación única. Si esto le incomoda, puede consultar la biblioteca MockContractopens in a new tab. Le permite sobrescribir y cambiar los comportamientos de los contratos sobre la marcha. Sin embargo, esto solo funciona para simular la activación de otro contrato, por lo que no funcionará para nuestro ejemplo.

La simulación puede ser aún más potente

Los poderes de la simulación no terminan aquí.

  • Añadir funciones: no solo sobrescribir una función específica es útil, también lo es añadir funciones adicionales. Un buen ejemplo para los tokens es añadir una función mint adicional que permita a cualquier usuario obtener nuevos tokens de forma gratuita.
  • Uso en redes de prueba: cuando implemente y pruebe sus contratos en redes de pruebas junto con su DApp, considere el usar una versión simulada. Evita el tener que sobreescribir las funciones, a menos que sea realmente necesario. Al fin y al cabo, se trata de probar la lógica real. Pero agregar, por ejemplo, una función de reinicio puede ser útil para simplemente restablecer el contrato a su estado inicial, sin requerir un nuevo despliegue. Obviamente, no haría eso en un contrato de red principal.

Última actualización de la página: 25 de agosto de 2025

¿Le ha resultado útil este tutorial?