Probar el contrato inteligente de forma sencilla con la biblioteca Waffle
En este tutorial aprenderá a:
- Evalúa los cambios en el saldo de la billetera
- Probar la emisión de eventos con argumentos especificos.
- Verificar que una transacción se ha revertido.
Supuestos
- Puede crear un nuevo proyecto JavaScript o TypeScript.
- Tiene experiencia básica con las pruebas en JavaScript.
- Ha utilizado algunos gestores de paquetes como yarn o npm.
- Posee conocimientos muy básicos de contratos inteligentes y Solidity.
Introducción
Este tutorial explica una configuración de prueba y se ejecuta utilizando yarn, pero no pasa nada si prefiere hacerlo con npm, ya proporcionaré las indicaciones apropiadas a la documentación oficial de Waffle(opens in a new tab).
Instalar las dependencias
Añada(opens in a new tab) las dependencias ethereum-waffle y de typescript a las dependencias de desarrollo de su proyecto.
yarn add --dev ethereum-waffle ts-node typescript @types/jest
Ejemplo de contrato inteligente
Durante el tutorial trabajaremos con un sencillo ejemplo de contrato inteligente: EtherSplitter. Permite casi se manera similar que cualquiera envíe algunos wei y, eventualmente, los divida entre dos receptores por defecto. La función dividida requiere que la cantidad de wei sea un número entero, de lo contario se revertirá. Para ambos receptores realiza una transferencia de wei, seguida a la emisión de un evento de transferencia.
Añada el fragmento de código de EtherSplitter en src/EtherSplitter.sol
.
1pragma solidity ^0.6.0;23contract EtherSplitter {4 address payable receiver1;5 address payable receiver2;67 event Transfer(address from, address to, uint256 amount);89 constructor(address payable _address1, address payable _address2) public {10 receiver1 = _address1;11 receiver2 = _address2;12 }1314 function split() public payable {15 require(msg.value % 2 == 0, 'Uneven wei amount not allowed');16 receiver1.transfer(msg.value / 2);17 emit Transfer(msg.sender, receiver1, msg.value / 2);18 receiver2.transfer(msg.value / 2);19 emit Transfer(msg.sender, receiver2, msg.value / 2);20 }21}Mostrar todoCopiar
Compilar el contrato
Para compilar(opens in a new tab) el contrato, añada las siguientes líneas al archivo de package.json:
1"scripts": {2 "build": "waffle"3 }Copiar
A continuación, cree el archivo de configuración de Waffle en el directorio de raíz del proyecto waffle.json
y después pegue ahí la siguiente configuración:
1{2 "compilerType": "solcjs",3 "compilerVersion": "0.6.2",4 "sourceDirectory": "./src",5 "outputDirectory": "./build"6}Copiar
Ejecute yarn build
. Como resultado, el directorio build
aparecerá con el contrato compilado de EtherSplitter en formato JSON.
Configuración de la prueba
Para hacer pruebas con Waffle se tienen que utilizar los emparejadores de Chai y Mocha, por lo que necesita añadirlos(opens in a new tab) a su proyecto. Actualice su archivo package.json y añada la línea de test
en la parte de scripts:
1"scripts": {2 "build": "waffle",3 "test": "export NODE_ENV=test && mocha -r ts-node/register 'test/**/*.test.ts'"4 }Copiar
Si quiere ejecutar(opens in a new tab) sus pruebas, simplemente ejecute yarn test
.
Pruebas
Seguidamente cree el directorio test
y un nuevo archivo test\EtherSplitter.test.ts
. Copie el fragmento de abajo y péguelo en nuestro archivo de prueba.
1import { expect, use } from "chai"2import { Contract } from "ethers"3import { deployContract, MockProvider, solidity } from "ethereum-waffle"4import EtherSplitter from "../build/EtherSplitter.json"56use(solidity)78describe("Ether Splitter", () => {9 const [sender, receiver1, receiver2] = new MockProvider().getWallets()10 let splitter: Contract1112 beforeEach(async () => {13 splitter = await deployContract(sender, EtherSplitter, [14 receiver1.address,15 receiver2.address,16 ])17 })1819 // add the tests here20})Mostrar todo
Unas breves aclaraciones antes de que empecemos. El MockProvider
incluye una simulación de la cadena de bloques. También permite que las carteras simuladas funcionen para hacer la prueba del contrato EtherSplitter. Podemos obtener hasta diez carteras aplicando el método de getWallet()
en el proveedor. En el ejemplo, obtendremos tres carteras para el emisor y dos receptores.
A continuación, declaramos una variable llamada «splitter»: este es nuestro contrato simulado EtherSplitter. Se crea antes de la ejecución de una única prueba a través del método deployContract
. Este método simula la implementación de un contrato desde la cartera pasado como el primer parámetro (en nuestro caso, la cartera del emisor). El segundo parámetro es el ABI y el código de bytes del contrato de prueba: aquí pasamos el archivo json o el contrato EtherSplitter compilado desde el directorio build
. El tercer parámetro es una matriz con los argumentos del constructor del contrato, que en nuestro caso, son las dos direcciones de los receptores.
ChangeBalances
Primero, revisamos si el método dividido realmente cambia el saldo de las carteras de los receptores. Si dividimos 50 wei desde las cuentas de los emisores, esperaríamos que los saldos de ambos receptores aumentaran 25 wei. Usaremos el comparador changeBalances
de Waffle:
1it("Changes accounts balances", async () => {2 await expect(() => splitter.split({ value: 50 })).to.changeBalances(3 [receiver1, receiver2],4 [25, 25]5 )6})
Como primer parámetro del emparejador, enviamos una matriz de carteras receptoras; y como segundo parámetro, una matriz de aumentos esperados en las cuentas correspondientes. Si queremos revisar el balance de una billetera en específico, también podemos usar el comparador changeBalance
, que no requiere el envío de matrices como en el ejemplo anterior:
1it("Changes account balance", async () => {2 await expect(() => splitter.split({ value: 50 })).to.changeBalance(3 receiver1,4 255 )6})
Tenga en cuenta que en ambos casos changeBalance
y changeBalances
, enviamos la función dividida como devolución de llamada, porque el emparejador necesita acceder al estado de saldos antes y después de la llamada.
A continuación, probaremos si el evento de transferencia se emitió después de cada transferencia de wei. Cambiaremos a otra máquina desde Waffle:
Emit
1it("Emits event on the transfer to the first receiver", async () => {2 await expect(splitter.split({ value: 50 }))3 .to.emit(splitter, "Transfer")4 .withArgs(sender.address, receiver1.address, 25)5})67it("Emits event on the transfer to the second receiver", async () => {8 await expect(splitter.split({ value: 50 }))9 .to.emit(splitter, "Transfer")10 .withArgs(sender.address, receiver2.address, 25)11})Mostrar todo
El emparejador emit
nos permite revisar si un contrato ha emitido un evento al recurrir a un método. Como parámetros del emparejador emit
, proporcionamos el simulacro de contrato que predecimos para emitir el evento, junto al nombre de ese evento. En nuestro caso, el simulacro de contrato es splitter
y el nombre del evento: Transfer
. También podemos verificar los valores precisos de los argumentos con los que se emitió el evento: enviamos tantos argumentos al emparejador withArgs
como lo espera nuestra declaración de evento. En el caso del contrato EtherSplitter, enviamos las direcciones del emisor y del receptor, junto a la cantidad de wei transferida.
revertedWith
Como último ejemplo, comprobaremos si se revirtió la transacción en caso de número impar de wei. Usaremos el emparejador revertedWith
:
1it("Reverts when Vei amount uneven", async () => {2 await expect(splitter.split({ value: 51 })).to.be.revertedWith(3 "Uneven wei amount not allowed"4 )5})
Si la prueba sale bien, nos garantizará que se ha revertido la transacción. Sin embargo, también debe haber una coincidencia exacta entre los mensajes que hemos enviado en la declaración require
y el mensaje que esperamos en revertedWith
. Si regresamos al código del contrato EtherSplitter, en la declaración require
para la cantidad de wei, proporcionamos el mensaje: «no se permite una cantidad impar de wei». Esto coincide con el mensaje que esperamos en nuestra prueba. Si no coinciden, será que la prueba ha salido mal.
¡Felicitaciones!
¡Acabas de dar tu primer gran paso para probar los contratos inteligentes con Waffle! Puede que también te interesen otros tutoriales de Waffle:
- Probar ERC20 con Waffle
- Waffle: Llamadas dinámicas de simulación y prueba de contratos
- Tutorial de Waffle "Hola, Mundo" con hardhat y ethers
Última edición: @nhsz(opens in a new tab), 27 de febrero de 2024