ASD

Web3 Testing

Testea contratos inteligentes de forma exhaustiva con Hardhat y Foundry: tests unitarios, de integración y forking de mainnet.

Estrellas
38.8k

en todo el repo

Actividad
47

0–100, la ruta de este skill

Actualizado
hace 2 meses

último commit aquí

Commits
1

últimos 90 días

Contexto
2k tok

55 tok en reposo

Paquete
2 archivos

10 KB

Instalar

Funciona con cualquier agente que lea SKILL.md

npx -y skills add wshobson/agents --skill web3-testing --agent claude-code

Se instala solo en este repositorio.

Este skill makes network requests, needs API credentials.

Qué hace

  • Configura entornos de test para contratos inteligentes con Hardhat (hardhat.config.js con forking, gas reporter, Etherscan) y Foundry
  • Escribe tests unitarios con fixtures, eventos y comprobaciones de balances usando Chai/Hardhat
  • Genera tests de Foundry (Forge) con vm.prank, vm.deal, fuzzing y forks de mainnet
  • Implementa patrones avanzados: snapshot/revert de estado, forking de mainnet e impersonación de cuentas

Úsalo cuando

  • Escribir tests unitarios para contratos inteligentes
  • Configurar suites de tests de integración
  • Realizar tests de optimización de gas
  • Hacer fuzzing para casos límite, forkear mainnet o verificar contratos en Etherscan

No lo uses cuando

    Qué lo activa

    Di cualquiera de estas frases y el agente debería cargar este skill.

    • Escribe tests unitarios en Hardhat para mi contrato Token
    • Configura un test de Foundry con fork de mainnet para interactuar con Uniswap
    • Ayúdame a hacer fuzzing de la función transfer con Forge

    SKILL.md

    En inglés

    Web3 Smart Contract Testing

    Master comprehensive testing strategies for smart contracts using Hardhat, Foundry, and advanced testing patterns.

    When to Use This Skill

    • Writing unit tests for smart contracts
    • Setting up integration test suites
    • Performing gas optimization testing
    • Fuzzing for edge cases
    • Forking mainnet for realistic testing
    • Automating test coverage reporting
    • Verifying contracts on Etherscan

    Hardhat Testing Setup

    // hardhat.config.js
    require("@nomicfoundation/hardhat-toolbox");
    require("@nomiclabs/hardhat-etherscan");
    require("hardhat-gas-reporter");
    require("solidity-coverage");
    
    module.exports = {
      solidity: {
        version: "0.8.19",
        settings: {
          optimizer: {
            enabled: true,
            runs: 200,
          },
        },
      },
      networks: {
        hardhat: {
          forking: {
            url: process.env.MAINNET_RPC_URL,
            blockNumber: 15000000,
          },
        },
        goerli: {
          url: process.env.GOERLI_RPC_URL,
          accounts: [process.env.PRIVATE_KEY],
        },
      },
      gasReporter: {
        enabled: true,
        currency: "USD",
        coinmarketcap: process.env.COINMARKETCAP_API_KEY,
      },
      etherscan: {
        apiKey: process.env.ETHERSCAN_API_KEY,
      },
    };
    

    Unit Testing Patterns

    const { expect } = require("chai");
    const { ethers } = require("hardhat");
    const {
      loadFixture,
      time,
    } = require("@nomicfoundation/hardhat-network-helpers");
    
    describe("Token Contract", function () {
      // Fixture for test setup
      async function deployTokenFixture() {
        const [owner, addr1, addr2] = await ethers.getSigners();
    
        const Token = await ethers.getContractFactory("Token");
        const token = await Token.deploy();
    
        return { token, owner, addr1, addr2 };
      }
    
      describe("Deployment", function () {
        it("Should set the right owner", async function () {
          const { token, owner } = await loadFixture(deployTokenFixture);
          expect(await token.owner()).to.equal(owner.address);
        });
    
        it("Should assign total supply to owner", async function () {
          const { token, owner } = await loadFixture(deployTokenFixture);
          const ownerBalance = await token.balanceOf(owner.address);
          expect(await token.totalSupply()).to.equal(ownerBalance);
        });
      });
    
      describe("Transactions", function () {
        it("Should transfer tokens between accounts", async function () {
          const { token, owner, addr1 } = await loadFixture(deployTokenFixture);
    
          await expect(token.transfer(addr1.address, 50)).to.changeTokenBalances(
            token,
            [owner, addr1],
            [-50, 50],
          );
        });
    
        it("Should fail if sender doesn't have enough tokens", async function () {
          const { token, addr1 } = await loadFixture(deployTokenFixture);
          const initialBalance = await token.balanceOf(addr1.address);
    
          await expect(
            token.connect(addr1).transfer(owner.address, 1),
          ).to.be.revertedWith("Insufficient balance");
        });
    
        it("Should emit Transfer event", async function () {
          const { token, owner, addr1 } = await loadFixture(deployTokenFixture);
    
          await expect(token.transfer(addr1.address, 50))
            .to.emit(token, "Transfer")
            .withArgs(owner.address, addr1.address, 50);
        });
      });
    
      describe("Time-based tests", function () {
        it("Should handle time-locked operations", async function () {
          const { token } = await loadFixture(deployTokenFixture);
    
          // Increase time by 1 day
          await time.increase(86400);
    
          // Test time-dependent functionality
        });
      });
    
      describe("Gas optimization", function () {
        it("Should use gas efficiently", async function () {
          const { token } = await loadFixture(deployTokenFixture);
    
          const tx = await token.transfer(addr1.address, 100);
          const receipt = await tx.wait();
    
          expect(receipt.gasUsed).to.be.lessThan(50000);
        });
      });
    });
    

    Foundry Testing (Forge)

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    
    import "forge-std/Test.sol";
    import "../src/Token.sol";
    
    contract TokenTest is Test {
        Token token;
        address owner = address(1);
        address user1 = address(2);
        address user2 = address(3);
    
        function setUp() public {
            vm.prank(owner);
            token = new Token();
        }
    
        function testInitialSupply() public {
            assertEq(token.totalSupply(), 1000000 * 10**18);
        }
    
        function testTransfer() public {
            vm.prank(owner);
            token.transfer(user1, 100);
    
            assertEq(token.balanceOf(user1), 100);
            assertEq(token.balanceOf(owner), token.totalSupply() - 100);
        }
    
        function testFailTransferInsufficientBalance() public {
            vm.prank(user1);
            token.transfer(user2, 100); // Should fail
        }
    
        function testCannotTransferToZeroAddress() public {
            vm.prank(owner);
            vm.expectRevert("Invalid recipient");
            token.transfer(address(0), 100);
        }
    
        // Fuzzing test
        function testFuzzTransfer(uint256 amount) public {
            vm.assume(amount > 0 && amount <= token.totalSupply());
    
            vm.prank(owner);
            token.transfer(user1, amount);
    
            assertEq(token.balanceOf(user1), amount);
        }
    
        // Test with cheatcodes
        function testDealAndPrank() public {
            // Give ETH to address
            vm.deal(user1, 10 ether);
    
            // Impersonate address
            vm.prank(user1);
    
            // Test functionality
            assertEq(user1.balance, 10 ether);
        }
    
        // Mainnet fork test
        function testForkMainnet() public {
            vm.createSelectFork("https://eth-mainnet.alchemyapi.io/v2/...");
    
            // Interact with mainnet contracts
            address dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
            assertEq(IERC20(dai).symbol(), "DAI");
        }
    }
    

    Advanced Testing Patterns

    Snapshot and Revert

    describe("Complex State Changes", function () {
      let snapshotId;
    
      beforeEach(async function () {
        snapshotId = await network.provider.send("evm_snapshot");
      });
    
      afterEach(async function () {
        await network.provider.send("evm_revert", [snapshotId]);
      });
    
      it("Test 1", async function () {
        // Make state changes
      });
    
      it("Test 2", async function () {
        // State reverted, clean slate
      });
    });
    

    Mainnet Forking

    describe("Mainnet Fork Tests", function () {
      let uniswapRouter, dai, usdc;
    
      before(async function () {
        await network.provider.request({
          method: "hardhat_reset",
          params: [
            {
              forking: {
                jsonRpcUrl: process.env.MAINNET_RPC_URL,
                blockNumber: 15000000,
              },
            },
          ],
        });
    
        // Connect to existing mainnet contracts
        uniswapRouter = await ethers.getContractAt(
          "IUniswapV2Router",
          "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
        );
    
        dai = await ethers.getContractAt(
          "IERC20",
          "0x6B175474E89094C44Da98b954EedeAC495271d0F",
        );
      });
    
      it("Should swap on Uniswap", async function () {
        // Test with real Uniswap contracts
      });
    });
    

    Impersonating Accounts

    it("Should impersonate whale account", async function () {
      const whaleAddress = "0x...";
    
      await network.provider.request({
        method: "hardhat_impersonateAccount",
        params: [whaleAddress],
      });
    
      const whale = await ethers.getSigner(whaleAddress);
    
      // Use whale's tokens
      await dai
        .connect(whale)
        .transfer(addr1.address, ethers.utils.parseEther("1000"));
    });
    

    Additional patterns and templates

    More detailed templates and worked examples live in references/details.md. Read that file for the full pattern library.

    Reproducido de wshobson/agents bajo licencia MIT. Leer esta página en markdown.

    Archivos

    2 archivos en el paquete. Solo se lee SKILL.md al activarse — las referencias se cargan si el skill decide que las necesita.

    Antes de instalar

    Requiere Hardhat o Foundry instalados, y variables de entorno como MAINNET_RPC_URL, PRIVATE_KEY y ETHERSCAN_API_KEY.

    Variables de entorno:COINMARKETCAP_API_KEYETHERSCAN_API_KEYGOERLI_RPC_URLMAINNET_RPC_URLPRIVATE_KEY

    Detalles

    Creador
    wshobson
    Categoría
    Testing y QA
    Licencia
    MIT
    Recursos incluidos
    referencias
    Repositorio
    wshobson/agents
    Código fuente
    Ver SKILL.md

    Etiquetas

    Más de wshobson/agents

    Este repo incluye 180 skills. Si instalas uno, normalmente ya tienes los demás.

    Úsalo al seleccionar y colocar iconos, imágenes, SVGs, diagramas o infografías de apoyo aprobados en un PPTX editable.

    Costo de contexto al activarse
    344 tok
    Tamaño del paquete
    2 archivos
    Última actualización
    hace 26 días
    documentos

    Úsalo cuando pidan optimizar un prompt, mejorar su rendimiento, diseñar una plantilla, aplicar chain-of-thought, few-shot prompting o técnicas avanzadas de prompt engineering para producción.

    Costo de contexto al activarse
    1.3k tok
    Tamaño del paquete
    10 archivos
    Última actualización
    el mes pasado
    herramientas desarrollo

    Úsalo al redactar o reparar una especificación JSON con coordenadas explícitas para un PPTX editable.

    Costo de contexto al activarse
    489 tok
    Tamaño del paquete
    2 archivos
    Última actualización
    hace 26 días
    documentos

    Úsalo para validar o reparar un PPTX editable en cuanto a geometría, accesibilidad, editabilidad nativa, linaje de fuente e integridad del paquete OOXML.

    Costo de contexto al activarse
    409 tok
    Tamaño del paquete
    2 archivos
    Última actualización
    hace 26 días
    documentos

    Úsalo para analizar un PPTX de referencia en modo solo lectura: estructura, tema, tipografía, ritmo de layout, diagnósticos, catálogos de plantillas derivados o inspección segura del paquete OOXML.

    Costo de contexto al activarse
    689 tok
    Tamaño del paquete
    8 archivos
    Última actualización
    hace 26 días
    documentos

    Úsalo al preparar la narrativa, las fuentes y el contexto de diseño para un nuevo deck PPTX editable.

    Costo de contexto al activarse
    415 tok
    Tamaño del paquete
    2 archivos
    Última actualización
    hace 26 días
    documentos

    Skills relacionados

    Realiza auditorías de accesibilidad WCAG 2.2 con pruebas automatizadas, verificación manual y guía de remediación. Útil para auditar sitios, corregir violaciones y aplicar patrones de diseño accesible.

    Costo de contexto al activarse
    628 tok
    Tamaño del paquete
    2 archivos
    Última actualización
    hace 2 meses
    testing qa

    Prueba workflows de Temporal con pytest, time-skipping y estrategias de mocking: testing unitario, de integración, de replay y configuración de desarrollo local.

    Costo de contexto al activarse
    1.2k tok
    Tamaño del paquete
    5 archivos
    Última actualización
    hace 3 meses
    testing qa

    Úsalo al revisar código Python en busca de antipatrones comunes: como checklist antes de finalizar implementaciones o al depurar problemas derivados de malas prácticas conocidas.

    Costo de contexto al activarse
    2k tok
    Tamaño del paquete
    1 archivo
    Última actualización
    hace 4 meses
    testing qa