[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71TM834ZNJDFT968HRT64W5":3,"skill-same:01M71TM834ZNJDFT968HRT64W5":68},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":43,"linkOnly":44,"markdownContent":45,"markdownBody":46,"frontMatter":47,"refName":48,"commitSha":49,"skillDirectory":50,"forksCount":13,"repositoryHtmlUrl":51,"repositoryHomepageUrl":6,"ownerHtmlUrl":52,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":53,"install":54,"resources":67},"01M71TM834ZNJDFT968HRT64W5","web3-testing",null,"Test smart contracts comprehensively using Hardhat and Foundry with unit tests, integration tests, and mainnet forking. Use when testing Solidity contracts, setting up blockchain test suites, or va...","javiertarazon\u002Fide-agente-free-jt7-","javiertarazon","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F153141207?v=4","01M71R52TB3S9HXKH6WSNT8A7V","01M71R52VG5KS2DKDDH4GDQ30A",0,"MIT","https:\u002F\u002Fgithub.com\u002Fjaviertarazon\u002Fide-agente-free-jt7-\u002Fblob\u002Frelease\u002Fv4.2.11-panel-pro\u002F.github\u002Fskills\u002Fweb3-testing\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42],"web3","web3 testing","testing","solidity","foundry","VA","integration","test","with","unit tests","unknown","when","use","tests","up","hardhat","integration tests","setting","community","unit","2026-11-10 13:02:02",false,"---\nname: web3-testing\ndescription: \"Test smart contracts comprehensively using Hardhat and Foundry with unit tests, integration tests, and mainnet forking. Use when testing Solidity contracts, setting up blockchain test suites, or va...\"\nrisk: unknown\nsource: community\n---\n\n# Web3 Smart Contract Testing\n\nMaster comprehensive testing strategies for smart contracts using Hardhat, Foundry, and advanced testing patterns.\n\n## Do not use this skill when\n\n- The task is unrelated to web3 smart contract testing\n- You need a different domain or tool outside this scope\n\n## Instructions\n\n- Clarify goals, constraints, and required inputs.\n- Apply relevant best practices and validate outcomes.\n- Provide actionable steps and verification.\n- If detailed examples are required, open `resources\u002Fimplementation-playbook.md`.\n\n## Use this skill when\n\n- Writing unit tests for smart contracts\n- Setting up integration test suites\n- Performing gas optimization testing\n- Fuzzing for edge cases\n- Forking mainnet for realistic testing\n- Automating test coverage reporting\n- Verifying contracts on Etherscan\n\n## Hardhat Testing Setup\n\n```javascript\n\u002F\u002F hardhat.config.js\nrequire(\"@nomicfoundation\u002Fhardhat-toolbox\");\nrequire(\"@nomiclabs\u002Fhardhat-etherscan\");\nrequire(\"hardhat-gas-reporter\");\nrequire(\"solidity-coverage\");\n\nmodule.exports = {\n  solidity: {\n    version: \"0.8.19\",\n    settings: {\n      optimizer: {\n        enabled: true,\n        runs: 200,\n      },\n    },\n  },\n  networks: {\n    hardhat: {\n      forking: {\n        url: process.env.MAINNET_RPC_URL,\n        blockNumber: 15000000,\n      },\n    },\n    goerli: {\n      url: process.env.GOERLI_RPC_URL,\n      accounts: [process.env.PRIVATE_KEY],\n    },\n  },\n  gasReporter: {\n    enabled: true,\n    currency: \"USD\",\n    coinmarketcap: process.env.COINMARKETCAP_API_KEY,\n  },\n  etherscan: {\n    apiKey: process.env.ETHERSCAN_API_KEY,\n  },\n};\n```\n\n## Unit Testing Patterns\n\n```javascript\nconst { expect } = require(\"chai\");\nconst { ethers } = require(\"hardhat\");\nconst {\n  loadFixture,\n  time,\n} = require(\"@nomicfoundation\u002Fhardhat-network-helpers\");\n\ndescribe(\"Token Contract\", function () {\n  \u002F\u002F Fixture for test setup\n  async function deployTokenFixture() {\n    const [owner, addr1, addr2] = await ethers.getSigners();\n\n    const Token = await ethers.getContractFactory(\"Token\");\n    const token = await Token.deploy();\n\n    return { token, owner, addr1, addr2 };\n  }\n\n  describe(\"Deployment\", function () {\n    it(\"Should set the right owner\", async function () {\n      const { token, owner } = await loadFixture(deployTokenFixture);\n      expect(await token.owner()).to.equal(owner.address);\n    });\n\n    it(\"Should assign total supply to owner\", async function () {\n      const { token, owner } = await loadFixture(deployTokenFixture);\n      const ownerBalance = await token.balanceOf(owner.address);\n      expect(await token.totalSupply()).to.equal(ownerBalance);\n    });\n  });\n\n  describe(\"Transactions\", function () {\n    it(\"Should transfer tokens between accounts\", async function () {\n      const { token, owner, addr1 } = await loadFixture(deployTokenFixture);\n\n      await expect(token.transfer(addr1.address, 50)).to.changeTokenBalances(\n        token,\n        [owner, addr1],\n        [-50, 50],\n      );\n    });\n\n    it(\"Should fail if sender doesn't have enough tokens\", async function () {\n      const { token, addr1 } = await loadFixture(deployTokenFixture);\n      const initialBalance = await token.balanceOf(addr1.address);\n\n      await expect(\n        token.connect(addr1).transfer(owner.address, 1),\n      ).to.be.revertedWith(\"Insufficient balance\");\n    });\n\n    it(\"Should emit Transfer event\", async function () {\n      const { token, owner, addr1 } = await loadFixture(deployTokenFixture);\n\n      await expect(token.transfer(addr1.address, 50))\n        .to.emit(token, \"Transfer\")\n        .withArgs(owner.address, addr1.address, 50);\n    });\n  });\n\n  describe(\"Time-based tests\", function () {\n    it(\"Should handle time-locked operations\", async function () {\n      const { token } = await loadFixture(deployTokenFixture);\n\n      \u002F\u002F Increase time by 1 day\n      await time.increase(86400);\n\n      \u002F\u002F Test time-dependent functionality\n    });\n  });\n\n  describe(\"Gas optimization\", function () {\n    it(\"Should use gas efficiently\", async function () {\n      const { token } = await loadFixture(deployTokenFixture);\n\n      const tx = await token.transfer(addr1.address, 100);\n      const receipt = await tx.wait();\n\n      expect(receipt.gasUsed).to.be.lessThan(50000);\n    });\n  });\n});\n```\n\n## Foundry Testing (Forge)\n\n```solidity\n\u002F\u002F SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std\u002FTest.sol\";\nimport \"..\u002Fsrc\u002FToken.sol\";\n\ncontract TokenTest is Test {\n    Token token;\n    address owner = address(1);\n    address user1 = address(2);\n    address user2 = address(3);\n\n    function setUp() public {\n        vm.prank(owner);\n        token = new Token();\n    }\n\n    function testInitialSupply() public {\n        assertEq(token.totalSupply(), 1000000 * 10**18);\n    }\n\n    function testTransfer() public {\n        vm.prank(owner);\n        token.transfer(user1, 100);\n\n        assertEq(token.balanceOf(user1), 100);\n        assertEq(token.balanceOf(owner), token.totalSupply() - 100);\n    }\n\n    function testFailTransferInsufficientBalance() public {\n        vm.prank(user1);\n        token.transfer(user2, 100); \u002F\u002F Should fail\n    }\n\n    function testCannotTransferToZeroAddress() public {\n        vm.prank(owner);\n        vm.expectRevert(\"Invalid recipient\");\n        token.transfer(address(0), 100);\n    }\n\n    \u002F\u002F Fuzzing test\n    function testFuzzTransfer(uint256 amount) public {\n        vm.assume(amount > 0 && amount \u003C= token.totalSupply());\n\n        vm.prank(owner);\n        token.transfer(user1, amount);\n\n        assertEq(token.balanceOf(user1), amount);\n    }\n\n    \u002F\u002F Test with cheatcodes\n    function testDealAndPrank() public {\n        \u002F\u002F Give ETH to address\n        vm.deal(user1, 10 ether);\n\n        \u002F\u002F Impersonate address\n        vm.prank(user1);\n\n        \u002F\u002F Test functionality\n        assertEq(user1.balance, 10 ether);\n    }\n\n    \u002F\u002F Mainnet fork test\n    function testForkMainnet() public {\n        vm.createSelectFork(\"https:\u002F\u002Feth-mainnet.alchemyapi.io\u002Fv2\u002F...\");\n\n        \u002F\u002F Interact with mainnet contracts\n        address dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;\n        assertEq(IERC20(dai).symbol(), \"DAI\");\n    }\n}\n```\n\n## Advanced Testing Patterns\n\n### Snapshot and Revert\n\n```javascript\ndescribe(\"Complex State Changes\", function () {\n  let snapshotId;\n\n  beforeEach(async function () {\n    snapshotId = await network.provider.send(\"evm_snapshot\");\n  });\n\n  afterEach(async function () {\n    await network.provider.send(\"evm_revert\", [snapshotId]);\n  });\n\n  it(\"Test 1\", async function () {\n    \u002F\u002F Make state changes\n  });\n\n  it(\"Test 2\", async function () {\n    \u002F\u002F State reverted, clean slate\n  });\n});\n```\n\n### Mainnet Forking\n\n```javascript\ndescribe(\"Mainnet Fork Tests\", function () {\n  let uniswapRouter, dai, usdc;\n\n  before(async function () {\n    await network.provider.request({\n      method: \"hardhat_reset\",\n      params: [\n        {\n          forking: {\n            jsonRpcUrl: process.env.MAINNET_RPC_URL,\n            blockNumber: 15000000,\n          },\n        },\n      ],\n    });\n\n    \u002F\u002F Connect to existing mainnet contracts\n    uniswapRouter = await ethers.getContractAt(\n      \"IUniswapV2Router\",\n      \"0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D\",\n    );\n\n    dai = await ethers.getContractAt(\n      \"IERC20\",\n      \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n    );\n  });\n\n  it(\"Should swap on Uniswap\", async function () {\n    \u002F\u002F Test with real Uniswap contracts\n  });\n});\n```\n\n### Impersonating Accounts\n\n```javascript\nit(\"Should impersonate whale account\", async function () {\n  const whaleAddress = \"0x...\";\n\n  await network.provider.request({\n    method: \"hardhat_impersonateAccount\",\n    params: [whaleAddress],\n  });\n\n  const whale = await ethers.getSigner(whaleAddress);\n\n  \u002F\u002F Use whale's tokens\n  await dai\n    .connect(whale)\n    .transfer(addr1.address, ethers.utils.parseEther(\"1000\"));\n});\n```\n\n## Gas Optimization Testing\n\n```javascript\nconst { expect } = require(\"chai\");\n\ndescribe(\"Gas Optimization\", function () {\n  it(\"Compare gas usage between implementations\", async function () {\n    const Implementation1 =\n      await ethers.getContractFactory(\"OptimizedContract\");\n    const Implementation2 = await ethers.getContractFactory(\n      \"UnoptimizedContract\",\n    );\n\n    const contract1 = await Implementation1.deploy();\n    const contract2 = await Implementation2.deploy();\n\n    const tx1 = await contract1.doSomething();\n    const receipt1 = await tx1.wait();\n\n    const tx2 = await contract2.doSomething();\n    const receipt2 = await tx2.wait();\n\n    console.log(\"Optimized gas:\", receipt1.gasUsed.toString());\n    console.log(\"Unoptimized gas:\", receipt2.gasUsed.toString());\n\n    expect(receipt1.gasUsed).to.be.lessThan(receipt2.gasUsed);\n  });\n});\n```\n\n## Coverage Reporting\n\n```bash\n# Generate coverage report\nnpx hardhat coverage\n\n# Output shows:\n# File                | % Stmts | % Branch | % Funcs | % Lines |\n# -------------------|---------|----------|---------|---------|\n# contracts\u002FToken.sol |   100   |   90     |   100   |   95    |\n```\n\n## Contract Verification\n\n```javascript\n\u002F\u002F Verify on Etherscan\nawait hre.run(\"verify:verify\", {\n  address: contractAddress,\n  constructorArguments: [arg1, arg2],\n});\n```\n\n```bash\n# Or via CLI\nnpx hardhat verify --network mainnet CONTRACT_ADDRESS \"Constructor arg1\" \"arg2\"\n```\n\n## CI\u002FCD Integration\n\n```yaml\n# .github\u002Fworkflows\u002Ftest.yml\nname: Tests\n\non: [push, pull_request]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions\u002Fcheckout@v2\n      - uses: actions\u002Fsetup-node@v2\n        with:\n          node-version: \"16\"\n\n      - run: npm install\n      - run: npx hardhat compile\n      - run: npx hardhat test\n      - run: npx hardhat coverage\n\n      - name: Upload coverage to Codecov\n        uses: codecov\u002Fcodecov-action@v2\n```\n\n## Resources\n\n- **references\u002Fhardhat-setup.md**: Hardhat configuration guide\n- **references\u002Ffoundry-setup.md**: Foundry testing framework\n- **references\u002Ftest-patterns.md**: Testing best practices\n- **references\u002Fmainnet-forking.md**: Fork testing strategies\n- **references\u002Fcontract-verification.md**: Etherscan verification\n- **assets\u002Fhardhat-config.js**: Complete Hardhat configuration\n- **assets\u002Ftest-suite.js**: Comprehensive test examples\n- **assets\u002Ffoundry.toml**: Foundry configuration\n- **scripts\u002Ftest-contract.sh**: Automated testing script\n\n## Best Practices\n\n1. **Test Coverage**: Aim for >90% coverage\n2. **Edge Cases**: Test boundary conditions\n3. **Gas Limits**: Verify functions don't hit block gas limit\n4. **Reentrancy**: Test for reentrancy vulnerabilities\n5. **Access Control**: Test unauthorized access attempts\n6. **Events**: Verify event emissions\n7. **Fixtures**: Use fixtures to avoid code duplication\n8. **Mainnet Fork**: Test with real contracts\n9. **Fuzzing**: Use property-based testing\n10. **CI\u002FCD**: Automate testing on every commit\n","# Web3 Smart Contract Testing\n\nMaster comprehensive testing strategies for smart contracts using Hardhat, Foundry, and advanced testing patterns.\n\n## Do not use this skill when\n\n- The task is unrelated to web3 smart contract testing\n- You need a different domain or tool outside this scope\n\n## Instructions\n\n- Clarify goals, constraints, and required inputs.\n- Apply relevant best practices and validate outcomes.\n- Provide actionable steps and verification.\n- If detailed examples are required, open `resources\u002Fimplementation-playbook.md`.\n\n## Use this skill when\n\n- Writing unit tests for smart contracts\n- Setting up integration test suites\n- Performing gas optimization testing\n- Fuzzing for edge cases\n- Forking mainnet for realistic testing\n- Automating test coverage reporting\n- Verifying contracts on Etherscan\n\n## Hardhat Testing Setup\n\n```javascript\n\u002F\u002F hardhat.config.js\nrequire(\"@nomicfoundation\u002Fhardhat-toolbox\");\nrequire(\"@nomiclabs\u002Fhardhat-etherscan\");\nrequire(\"hardhat-gas-reporter\");\nrequire(\"solidity-coverage\");\n\nmodule.exports = {\n  solidity: {\n    version: \"0.8.19\",\n    settings: {\n      optimizer: {\n        enabled: true,\n        runs: 200,\n      },\n    },\n  },\n  networks: {\n    hardhat: {\n      forking: {\n        url: process.env.MAINNET_RPC_URL,\n        blockNumber: 15000000,\n      },\n    },\n    goerli: {\n      url: process.env.GOERLI_RPC_URL,\n      accounts: [process.env.PRIVATE_KEY],\n    },\n  },\n  gasReporter: {\n    enabled: true,\n    currency: \"USD\",\n    coinmarketcap: process.env.COINMARKETCAP_API_KEY,\n  },\n  etherscan: {\n    apiKey: process.env.ETHERSCAN_API_KEY,\n  },\n};\n```\n\n## Unit Testing Patterns\n\n```javascript\nconst { expect } = require(\"chai\");\nconst { ethers } = require(\"hardhat\");\nconst {\n  loadFixture,\n  time,\n} = require(\"@nomicfoundation\u002Fhardhat-network-helpers\");\n\ndescribe(\"Token Contract\", function () {\n  \u002F\u002F Fixture for test setup\n  async function deployTokenFixture() {\n    const [owner, addr1, addr2] = await ethers.getSigners();\n\n    const Token = await ethers.getContractFactory(\"Token\");\n    const token = await Token.deploy();\n\n    return { token, owner, addr1, addr2 };\n  }\n\n  describe(\"Deployment\", function () {\n    it(\"Should set the right owner\", async function () {\n      const { token, owner } = await loadFixture(deployTokenFixture);\n      expect(await token.owner()).to.equal(owner.address);\n    });\n\n    it(\"Should assign total supply to owner\", async function () {\n      const { token, owner } = await loadFixture(deployTokenFixture);\n      const ownerBalance = await token.balanceOf(owner.address);\n      expect(await token.totalSupply()).to.equal(ownerBalance);\n    });\n  });\n\n  describe(\"Transactions\", function () {\n    it(\"Should transfer tokens between accounts\", async function () {\n      const { token, owner, addr1 } = await loadFixture(deployTokenFixture);\n\n      await expect(token.transfer(addr1.address, 50)).to.changeTokenBalances(\n        token,\n        [owner, addr1],\n        [-50, 50],\n      );\n    });\n\n    it(\"Should fail if sender doesn't have enough tokens\", async function () {\n      const { token, addr1 } = await loadFixture(deployTokenFixture);\n      const initialBalance = await token.balanceOf(addr1.address);\n\n      await expect(\n        token.connect(addr1).transfer(owner.address, 1),\n      ).to.be.revertedWith(\"Insufficient balance\");\n    });\n\n    it(\"Should emit Transfer event\", async function () {\n      const { token, owner, addr1 } = await loadFixture(deployTokenFixture);\n\n      await expect(token.transfer(addr1.address, 50))\n        .to.emit(token, \"Transfer\")\n        .withArgs(owner.address, addr1.address, 50);\n    });\n  });\n\n  describe(\"Time-based tests\", function () {\n    it(\"Should handle time-locked operations\", async function () {\n      const { token } = await loadFixture(deployTokenFixture);\n\n      \u002F\u002F Increase time by 1 day\n      await time.increase(86400);\n\n      \u002F\u002F Test time-dependent functionality\n    });\n  });\n\n  describe(\"Gas optimization\", function () {\n    it(\"Should use gas efficiently\", async function () {\n      const { token } = await loadFixture(deployTokenFixture);\n\n      const tx = await token.transfer(addr1.address, 100);\n      const receipt = await tx.wait();\n\n      expect(receipt.gasUsed).to.be.lessThan(50000);\n    });\n  });\n});\n```\n\n## Foundry Testing (Forge)\n\n```solidity\n\u002F\u002F SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std\u002FTest.sol\";\nimport \"..\u002Fsrc\u002FToken.sol\";\n\ncontract TokenTest is Test {\n    Token token;\n    address owner = address(1);\n    address user1 = address(2);\n    address user2 = address(3);\n\n    function setUp() public {\n        vm.prank(owner);\n        token = new Token();\n    }\n\n    function testInitialSupply() public {\n        assertEq(token.totalSupply(), 1000000 * 10**18);\n    }\n\n    function testTransfer() public {\n        vm.prank(owner);\n        token.transfer(user1, 100);\n\n        assertEq(token.balanceOf(user1), 100);\n        assertEq(token.balanceOf(owner), token.totalSupply() - 100);\n    }\n\n    function testFailTransferInsufficientBalance() public {\n        vm.prank(user1);\n        token.transfer(user2, 100); \u002F\u002F Should fail\n    }\n\n    function testCannotTransferToZeroAddress() public {\n        vm.prank(owner);\n        vm.expectRevert(\"Invalid recipient\");\n        token.transfer(address(0), 100);\n    }\n\n    \u002F\u002F Fuzzing test\n    function testFuzzTransfer(uint256 amount) public {\n        vm.assume(amount > 0 && amount \u003C= token.totalSupply());\n\n        vm.prank(owner);\n        token.transfer(user1, amount);\n\n        assertEq(token.balanceOf(user1), amount);\n    }\n\n    \u002F\u002F Test with cheatcodes\n    function testDealAndPrank() public {\n        \u002F\u002F Give ETH to address\n        vm.deal(user1, 10 ether);\n\n        \u002F\u002F Impersonate address\n        vm.prank(user1);\n\n        \u002F\u002F Test functionality\n        assertEq(user1.balance, 10 ether);\n    }\n\n    \u002F\u002F Mainnet fork test\n    function testForkMainnet() public {\n        vm.createSelectFork(\"https:\u002F\u002Feth-mainnet.alchemyapi.io\u002Fv2\u002F...\");\n\n        \u002F\u002F Interact with mainnet contracts\n        address dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;\n        assertEq(IERC20(dai).symbol(), \"DAI\");\n    }\n}\n```\n\n## Advanced Testing Patterns\n\n### Snapshot and Revert\n\n```javascript\ndescribe(\"Complex State Changes\", function () {\n  let snapshotId;\n\n  beforeEach(async function () {\n    snapshotId = await network.provider.send(\"evm_snapshot\");\n  });\n\n  afterEach(async function () {\n    await network.provider.send(\"evm_revert\", [snapshotId]);\n  });\n\n  it(\"Test 1\", async function () {\n    \u002F\u002F Make state changes\n  });\n\n  it(\"Test 2\", async function () {\n    \u002F\u002F State reverted, clean slate\n  });\n});\n```\n\n### Mainnet Forking\n\n```javascript\ndescribe(\"Mainnet Fork Tests\", function () {\n  let uniswapRouter, dai, usdc;\n\n  before(async function () {\n    await network.provider.request({\n      method: \"hardhat_reset\",\n      params: [\n        {\n          forking: {\n            jsonRpcUrl: process.env.MAINNET_RPC_URL,\n            blockNumber: 15000000,\n          },\n        },\n      ],\n    });\n\n    \u002F\u002F Connect to existing mainnet contracts\n    uniswapRouter = await ethers.getContractAt(\n      \"IUniswapV2Router\",\n      \"0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D\",\n    );\n\n    dai = await ethers.getContractAt(\n      \"IERC20\",\n      \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n    );\n  });\n\n  it(\"Should swap on Uniswap\", async function () {\n    \u002F\u002F Test with real Uniswap contracts\n  });\n});\n```\n\n### Impersonating Accounts\n\n```javascript\nit(\"Should impersonate whale account\", async function () {\n  const whaleAddress = \"0x...\";\n\n  await network.provider.request({\n    method: \"hardhat_impersonateAccount\",\n    params: [whaleAddress],\n  });\n\n  const whale = await ethers.getSigner(whaleAddress);\n\n  \u002F\u002F Use whale's tokens\n  await dai\n    .connect(whale)\n    .transfer(addr1.address, ethers.utils.parseEther(\"1000\"));\n});\n```\n\n## Gas Optimization Testing\n\n```javascript\nconst { expect } = require(\"chai\");\n\ndescribe(\"Gas Optimization\", function () {\n  it(\"Compare gas usage between implementations\", async function () {\n    const Implementation1 =\n      await ethers.getContractFactory(\"OptimizedContract\");\n    const Implementation2 = await ethers.getContractFactory(\n      \"UnoptimizedContract\",\n    );\n\n    const contract1 = await Implementation1.deploy();\n    const contract2 = await Implementation2.deploy();\n\n    const tx1 = await contract1.doSomething();\n    const receipt1 = await tx1.wait();\n\n    const tx2 = await contract2.doSomething();\n    const receipt2 = await tx2.wait();\n\n    console.log(\"Optimized gas:\", receipt1.gasUsed.toString());\n    console.log(\"Unoptimized gas:\", receipt2.gasUsed.toString());\n\n    expect(receipt1.gasUsed).to.be.lessThan(receipt2.gasUsed);\n  });\n});\n```\n\n## Coverage Reporting\n\n```bash\n# Generate coverage report\nnpx hardhat coverage\n\n# Output shows:\n# File                | % Stmts | % Branch | % Funcs | % Lines |\n# -------------------|---------|----------|---------|---------|\n# contracts\u002FToken.sol |   100   |   90     |   100   |   95    |\n```\n\n## Contract Verification\n\n```javascript\n\u002F\u002F Verify on Etherscan\nawait hre.run(\"verify:verify\", {\n  address: contractAddress,\n  constructorArguments: [arg1, arg2],\n});\n```\n\n```bash\n# Or via CLI\nnpx hardhat verify --network mainnet CONTRACT_ADDRESS \"Constructor arg1\" \"arg2\"\n```\n\n## CI\u002FCD Integration\n\n```yaml\n# .github\u002Fworkflows\u002Ftest.yml\nname: Tests\n\non: [push, pull_request]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions\u002Fcheckout@v2\n      - uses: actions\u002Fsetup-node@v2\n        with:\n          node-version: \"16\"\n\n      - run: npm install\n      - run: npx hardhat compile\n      - run: npx hardhat test\n      - run: npx hardhat coverage\n\n      - name: Upload coverage to Codecov\n        uses: codecov\u002Fcodecov-action@v2\n```\n\n## Resources\n\n- **references\u002Fhardhat-setup.md**: Hardhat configuration guide\n- **references\u002Ffoundry-setup.md**: Foundry testing framework\n- **references\u002Ftest-patterns.md**: Testing best practices\n- **references\u002Fmainnet-forking.md**: Fork testing strategies\n- **references\u002Fcontract-verification.md**: Etherscan verification\n- **assets\u002Fhardhat-config.js**: Complete Hardhat configuration\n- **assets\u002Ftest-suite.js**: Comprehensive test examples\n- **assets\u002Ffoundry.toml**: Foundry configuration\n- **scripts\u002Ftest-contract.sh**: Automated testing script\n\n## Best Practices\n\n1. **Test Coverage**: Aim for >90% coverage\n2. **Edge Cases**: Test boundary conditions\n3. **Gas Limits**: Verify functions don't hit block gas limit\n4. **Reentrancy**: Test for reentrancy vulnerabilities\n5. **Access Control**: Test unauthorized access attempts\n6. **Events**: Verify event emissions\n7. **Fixtures**: Use fixtures to avoid code duplication\n8. **Mainnet Fork**: Test with real contracts\n9. **Fuzzing**: Use property-based testing\n10. **CI\u002FCD**: Automate testing on every commit\n",{"name":5,"risk":33,"source":41,"description":7},"release\u002Fv4.2.11-panel-pro","a92ea02bcf7e2b3dbaf46d49696c29a3cb490d4f",".github\u002Fskills\u002Fweb3-testing","https:\u002F\u002Fgithub.com\u002Fjaviertarazon\u002Fide-agente-free-jt7-","https:\u002F\u002Fgithub.com\u002Fjaviertarazon","2026-07-12 07:10:45",{"source":8,"skillName":5,"commands":55,"prompt":65,"zipAvailable":66},[56,59,62],{"tool":57,"command":58},"npx","npx skills add javiertarazon\u002Fide-agente-free-jt7- --skill web3-testing",{"tool":60,"command":61},"bunx","bunx skills add javiertarazon\u002Fide-agente-free-jt7- --skill web3-testing",{"tool":63,"command":64},"pnpm","pnpm dlx skills add javiertarazon\u002Fide-agente-free-jt7- --skill web3-testing","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fjaviertarazon\u002Fide-agente-free-jt7-\u002Fblob\u002Frelease\u002Fv4.2.11-panel-pro\u002F.github\u002Fskills\u002Fweb3-testing\u002FSKILL.md",true,[],[69,94,121,141,156,178],{"skillNo":70,"name":71,"nameZh":6,"description":72,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":73,"categories":74,"keywords":78,"publishedAt":43},"01M71TQ7Q6Z10VWF3C2H5X1KW9","zustand-store-ts","Create Zustand stores with TypeScript, subscribeWithSelector middleware, and proper state\u002Faction separation. Use when building React state management, creating global stores, or implementing reacti...","https:\u002F\u002Fgithub.com\u002Fjaviertarazon\u002Fide-agente-free-jt7-\u002Fblob\u002Frelease\u002Fv4.2.11-panel-pro\u002F.github\u002Fskills\u002Fzustand-store-ts\u002FSKILL.md",[75],{"categoryNo":76,"categoryName":77,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000009","前端开发",[79,80,81,41,82,83,35,84,34,33,85,31,86,87,88,89,90,91,92,93],"zustand","store","zustand store ts","middleware","implementing","react state management","react","state","create","state management","typescript","action","management","separation","global",{"skillNo":95,"name":96,"nameZh":6,"description":97,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":98,"categories":99,"keywords":103,"publishedAt":43},"01M71TQ5EAR40G6W0MV5ZDVEZT","zoom-automation","Automate Zoom meeting creation, management, recordings, webinars, and participant tracking via Rube MCP (Composio). Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002Fjaviertarazon\u002Fide-agente-free-jt7-\u002Fblob\u002Frelease\u002Fv4.2.11-panel-pro\u002F.github\u002Fskills\u002Fzoom-automation\u002FSKILL.md",[100],{"categoryNo":101,"categoryName":102,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发",[104,105,106,41,107,108,109,110,111,33,112,113,114,115,116,117,118,119,91,120],"zoom automation","zoom","automation","creation","for","search","schemas","MCP","first","via","meeting","search tools","tracking","webinars","composio","recordings","Tools",{"skillNo":122,"name":123,"nameZh":6,"description":124,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":125,"categories":126,"keywords":128,"publishedAt":43},"01M71TQ29B1EYBCEAP1ZBGXQ1A","zoho-crm-automation","Automate Zoho CRM tasks via Rube MCP (Composio): create\u002Fupdate records, search contacts, manage leads, and convert leads. Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002Fjaviertarazon\u002Fide-agente-free-jt7-\u002Fblob\u002Frelease\u002Fv4.2.11-panel-pro\u002F.github\u002Fskills\u002Fzoho-crm-automation\u002FSKILL.md",[127],{"categoryNo":101,"categoryName":102,"taxonomyType":20,"taxonomyTypeDesc":21},[129,130,106,131,132,133,41,134,135,108,136,109,110,137,111,33,138,139,140,87],"crm automation","zoho crm automation","crm","zoho crm","zoho","leads","manage leads","manage","records","convert","tasks","contacts",{"skillNo":142,"name":143,"nameZh":6,"description":144,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":145,"categories":146,"keywords":148,"publishedAt":43},"01M71TPZPMN60DH0XV58KVHY23","zendesk-automation","Automate Zendesk tasks via Rube MCP (Composio): tickets, users, organizations, replies. Always search tools first for current schemas.","https:\u002F\u002Fgithub.com\u002Fjaviertarazon\u002Fide-agente-free-jt7-\u002Fblob\u002Frelease\u002Fv4.2.11-panel-pro\u002F.github\u002Fskills\u002Fzendesk-automation\u002FSKILL.md",[147],{"categoryNo":101,"categoryName":102,"taxonomyType":20,"taxonomyTypeDesc":21},[149,106,150,41,108,109,110,111,151,33,139,152,153,112,113,154,115,118,120,155],"zendesk automation","zendesk","organizations","replies","users","tickets","automate",{"skillNo":157,"name":158,"nameZh":6,"description":159,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":160,"categories":161,"keywords":162,"publishedAt":43},"01M71TPXDWKHKG4N6TVEPXWD0N","zapier-make-patterns","No-code automation democratizes workflow building. Zapier and Make (formerly Integromat) let non-developers automate business processes without writing code. But no-code doesn't mean no-complexity ...","https:\u002F\u002Fgithub.com\u002Fjaviertarazon\u002Fide-agente-free-jt7-\u002Fblob\u002Frelease\u002Fv4.2.11-panel-pro\u002F.github\u002Fskills\u002Fzapier-make-patterns\u002FSKILL.md",[],[163,164,165,166,167,168,169,170,171,106,172,33,173,174,175,176,177,155],"zapier make patterns","zapier","patterns","make","No-Code","complexity","writing","spawner","business","workflow","code","integromat","apache","processes","but",{"skillNo":179,"name":180,"nameZh":6,"description":181,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":182,"categories":183,"keywords":187,"publishedAt":43},"01M71TPT5T10WXNKW9G7ZXZ27W","youtube-summarizer","Extract transcripts from YouTube videos and generate comprehensive, detailed summaries using intelligent analysis frameworks","https:\u002F\u002Fgithub.com\u002Fjaviertarazon\u002Fide-agente-free-jt7-\u002Fblob\u002Frelease\u002Fv4.2.11-panel-pro\u002F.github\u002Fskills\u002Fyoutube-summarizer\u002FSKILL.md",[184],{"categoryNo":185,"categoryName":186,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000016","图像与多媒体",[188,189,190,191,41,192,193,194,195,196,197,198,173,199,200,201,202,203,204,205],"summarizer","youtube","youtube summarizer","Video","content-analysis","frameworks","copilot","2025","github-copilot","extract transcripts","analysis","codex","cli","summaries","github","generate","summarization","2026"]