$npx -y skills add wshobson/agents --skill solidity-securityMaster smart contract security best practices to prevent common vulnerabilities and implement secure Solidity patterns. Use when writing smart contracts, auditing existing contracts, or implementing security measures for blockchain applications.
| 1 | # Solidity Security |
| 2 | |
| 3 | Master smart contract security best practices, vulnerability prevention, and secure Solidity development patterns. |
| 4 | |
| 5 | ## When to Use This Skill |
| 6 | |
| 7 | - Writing secure smart contracts |
| 8 | - Auditing existing contracts for vulnerabilities |
| 9 | - Implementing secure DeFi protocols |
| 10 | - Preventing reentrancy, overflow, and access control issues |
| 11 | - Optimizing gas usage while maintaining security |
| 12 | - Preparing contracts for professional audits |
| 13 | - Understanding common attack vectors |
| 14 | |
| 15 | ## Detailed patterns and worked examples |
| 16 | |
| 17 | Detailed pattern documentation lives in `references/details.md`. Read that file when the navigation tier above is insufficient. |
| 18 | |
| 19 | ## Testing for Security |
| 20 | |
| 21 | ```javascript |
| 22 | // Hardhat test example |
| 23 | const { expect } = require("chai"); |
| 24 | const { ethers } = require("hardhat"); |
| 25 | |
| 26 | describe("Security Tests", function () { |
| 27 | it("Should prevent reentrancy attack", async function () { |
| 28 | const [attacker] = await ethers.getSigners(); |
| 29 | |
| 30 | const VictimBank = await ethers.getContractFactory("SecureBank"); |
| 31 | const bank = await VictimBank.deploy(); |
| 32 | |
| 33 | const Attacker = await ethers.getContractFactory("ReentrancyAttacker"); |
| 34 | const attackerContract = await Attacker.deploy(bank.address); |
| 35 | |
| 36 | // Deposit funds |
| 37 | await bank.deposit({ value: ethers.utils.parseEther("10") }); |
| 38 | |
| 39 | // Attempt reentrancy attack |
| 40 | await expect( |
| 41 | attackerContract.attack({ value: ethers.utils.parseEther("1") }), |
| 42 | ).to.be.revertedWith("ReentrancyGuard: reentrant call"); |
| 43 | }); |
| 44 | |
| 45 | it("Should prevent integer overflow", async function () { |
| 46 | const Token = await ethers.getContractFactory("SecureToken"); |
| 47 | const token = await Token.deploy(); |
| 48 | |
| 49 | // Attempt overflow |
| 50 | await expect(token.transfer(attacker.address, ethers.constants.MaxUint256)) |
| 51 | .to.be.reverted; |
| 52 | }); |
| 53 | |
| 54 | it("Should enforce access control", async function () { |
| 55 | const [owner, attacker] = await ethers.getSigners(); |
| 56 | |
| 57 | const Contract = await ethers.getContractFactory("SecureContract"); |
| 58 | const contract = await Contract.deploy(); |
| 59 | |
| 60 | // Attempt unauthorized withdrawal |
| 61 | await expect(contract.connect(attacker).withdraw(100)).to.be.revertedWith( |
| 62 | "Ownable: caller is not the owner", |
| 63 | ); |
| 64 | }); |
| 65 | }); |
| 66 | ``` |
| 67 | |
| 68 | ## Audit Preparation |
| 69 | |
| 70 | ```solidity |
| 71 | contract WellDocumentedContract { |
| 72 | /** |
| 73 | * @title Well Documented Contract |
| 74 | * @dev Example of proper documentation for audits |
| 75 | * @notice This contract handles user deposits and withdrawals |
| 76 | */ |
| 77 | |
| 78 | /// @notice Mapping of user balances |
| 79 | mapping(address => uint256) public balances; |
| 80 | |
| 81 | /** |
| 82 | * @dev Deposits ETH into the contract |
| 83 | * @notice Anyone can deposit funds |
| 84 | */ |
| 85 | function deposit() public payable { |
| 86 | require(msg.value > 0, "Must send ETH"); |
| 87 | balances[msg.sender] += msg.value; |
| 88 | } |
| 89 | |
| 90 | /** |
| 91 | * @dev Withdraws user's balance |
| 92 | * @notice Follows CEI pattern to prevent reentrancy |
| 93 | * @param amount Amount to withdraw in wei |
| 94 | */ |
| 95 | function withdraw(uint256 amount) public { |
| 96 | // CHECKS |
| 97 | require(amount <= balances[msg.sender], "Insufficient balance"); |
| 98 | |
| 99 | // EFFECTS |
| 100 | balances[msg.sender] -= amount; |
| 101 | |
| 102 | // INTERACTIONS |
| 103 | (bool success, ) = msg.sender.call{value: amount}(""); |
| 104 | require(success, "Transfer failed"); |
| 105 | } |
| 106 | } |
| 107 | ``` |