solidity-security
Skill1 starsChanged 8 months agoMITView on GitHub
Solidity smart contract security: vulnerability prevention, secure coding patterns, gas-safe optimizations, and audit preparation. Use when writing or reviewing Solidity code for security, auditing contracts, preventing reentrancy/overflow/access-control issues, optimizing gas safely, or preparing contracts for professional audits. Keywords: solidity security, smart contract audit, reentrancy, access control, CEI pattern, front-running, slither, invariant, vulnerability, exploit, secure solidity.
9 other projects use a version of this.
Adopted by
Most recently taken up by
- 0xlayerghost/solidity-agent-kittheir own version, first seen Oct 19, 2026
- xfstudio/skillstheir own version, first seen Oct 13, 2026
- sunshineluyao/Moss-Mcp-Transactiontheir own version, first seen Oct 13, 2026
- Samueladewole/claude-skillstheir own version, first seen Oct 13, 2026
- dorian-akhs/skillstheir own version, first seen Sep 1, 2026
- ai4brands-design/claude-skillstheir own version, first seen Mar 22, 2026
Other versions9 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 9
- wpank/ai★ 11same name
- xfstudio/skills★ 9same name
- 0xlayerghost/solidity-agent-kit★ 4same name
- ai4brands-design/claude-skills★ 2same name
- CodeWithBehnam/cc-docs★ 2same name
- whatthehackinsg/agent-auction★ 2same name
- sunshineluyao/Moss-Mcp-Transaction★ 1same name
- dorian-akhs/skills★ 0same name
- Samueladewole/claude-skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository33 other files it gives its agents.
- argocdSkill
- c-cpp-compilersSkill
- coding-guidelinesSkill
- devops-iac-engineerSkill
- dag-flowdSkill
- flowd-rules-authorSkill
- frontend-designSkill
- ghSkill
- grafana-dashboardsSkill
- helmSkill
- kustomizeSkill
- legalizerSkill
- lua-projectsSkill
- markdown-documentationSkill
- mermaid-diagramsSkill
- meta-cognition-parallelSkill
- podmasterSkill
- practical-haskellSkill
- rcaSkill
- researcherSkill
- security-auditSkill
- svg-genSkill
- tdd-red-green-refactorSkill
- tmux-masterySkill
- tokenomics-designSkill
- ultimate-dbSkill
- ultimate-nixosSkill
- ultimate-rustSkill
- ultimate-web3Skill
- web3-frontendSkill
- web3-testingSkill
- web3-whitepaperSkill
- ziglangSkill
The file
We split this skill into the rules it gives an agent
7 rules. 0 are common, 1 is distinctive, and 6 are very specific to this project.
0
Common
Most of the 6 edited versions we read keep these.
1
Distinctive
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 25
Never use Unicode box-drawing characters (─, ━, ═, etc.) as comment decorators or section separators in generated code.
- Very specific: line 112
Never tx.origin for auth - only msg.sender
- Very specific: line 128
Pre-0.8.0: Use SafeMath. There is no reason to target < 0.8.0 for new contracts.
- Very specific: line 183
Always SafeERC20 for token operations.
- Very specific: line 247
Use EIP-712 typed data + nonce + block.chainid in the domain separator.
- Very specific: line 311
Never sacrifice correctness for gas.
- Distinctive: line 356
Only persist to storage what on-chain logic actually reads.
“Common” is measured: kept by at least 6 in 10 of the 6 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Solidity Security
Vulnerability prevention, secure patterns, gas-safe optimizations, audit preparation.
A codebase audit, pen test, or findings report belongs to security-audit. Use this skill for Solidity patterns, secure fixes, and audit preparation.
Code Style Rules
No Unicode Separator Comments
Never use Unicode box-drawing characters (─, ━, ═, etc.) as comment decorators or section separators in generated code. This includes patterns like:
// ── State ─────────────────────────────────────────
// ══ Errors ═════════════════════════════════════════These are AI slop. They carry no semantic value, are invisible noise in diffs, and mark generated code as low-quality. Use plain labels or nothing at all:
// State
mapping(address => uint256) public balances;
// Errors
error InsufficientBalance();Vulnerabilities & Secure Patterns
1. Reentrancy
External call before state update lets an attacker re-enter mid-execution.
Vulnerable:
function withdraw() public {
uint256 amount = balances[msg.sender];
(bool ok, ) = msg.sender.call{value: amount}("");
require(ok);
balances[msg.sender] = 0; // state update after call
}Secure - CEI + ReentrancyGuard:
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
contract Vault is ReentrancyGuard {
// Errors
error InsufficientBalance();
error TransferFailed();
function withdraw(uint256 amount) external nonReentrant {
if (balances[msg.sender] < amount) revert InsufficientBalance();
balances[msg.sender] -= amount;
(bool ok, ) = msg.sender.call{value: amount}("");
if (!ok) revert TransferFailed();
}
}Cross-function reentrancy: attacker re-enters a different function that reads stale state. Apply nonReentrant to all functions sharing mutable state, not just the one with the external call.
2. Access Control
Vulnerable:
function withdraw(uint256 amount) public {
payable(msg.sender).transfer(amount);
}Secure:
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
// Two-step transfer prevents accidental ownership loss
contract SimpleAccess is Ownable2Step {
function emergencyWithdraw() external onlyOwner { /* ... */ }
}
// Role-based for multi-actor systems
contract RoleAccess is AccessControl {
bytes32 public constant OPERATOR = keccak256("OPERATOR");
function sensitiveOp() external onlyRole(OPERATOR) { /* ... */ }
}- Never
tx.originfor auth - onlymsg.sender Ownable2StepoverOwnable- Validate
address(0)on all address parameters
3. Integer Overflow / Underflow
Solidity >= 0.8.0 has checked arithmetic by default. For unchecked blocks, the surrounding logic must prove bounds:
uint256 len = arr.length;
for (uint256 i; i < len; ) {
// i < len < type(uint256).max, so ++i cannot overflow
unchecked { ++i; }
}Pre-0.8.0: Use SafeMath. There is no reason to target < 0.8.0 for new contracts.
4. Front-Running / MEV
Vulnerable:
function swap(uint256 amount, uint256 minOutput) public {
uint256 output = calculateOutput(amount);
require(output >= minOutput, "Slippage");
}Secure - Commit-Reveal:
// State
mapping(bytes32 => uint256) public commitBlock;
uint256 public constant REVEAL_DELAY = 1;
// Errors
error NoCommitment();
error RevealTooEarly();
function commit(bytes32 hash) external {
commitBlock[hash] = block.number;
}
function reveal(uint256 amount, uint256 minOutput, bytes32 secret) external {
bytes32 hash = keccak256(abi.encodePacked(msg.sender, amount, minOutput, secret));
if (commitBlock[hash] == 0) revert NoCommitment();
if (block.number <= commitBlock[hash] + REVEAL_DELAY) revert RevealTooEarly();
delete commitBlock[hash];
}Other mitigations: Flashbots Protect / MEV Blocker, slippage + deadline params, batch auctions (CoW Protocol).
5. Unchecked External Calls
Some tokens (USDT) don't return bool - raw .transfer() silently fails.
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract TokenVault {
using SafeERC20 for IERC20;
function send(IERC20 token, address to, uint256 amount) internal {
token.safeTransfer(to, amount);
}
}Always SafeERC20 for token operations.
6. Oracle Manipulation
| Risk | Mitigation |
| Spot price manipulation | TWAP over multiple blocks |
| Single oracle failure | Multiple independent oracles, median |
| Stale data | Freshness check on updatedAt |
| Flash loan attack | Chainlink latestRoundData + sanity bounds |
error InvalidPrice();
error StaleOracle();
uint256 public constant MAX_STALENESS = 1 hours;
function getPrice(AggregatorV3Interface feed) internal view returns (uint256) {
(, int256 price, , uint256 updatedAt, ) = feed.latestRoundData();
if (price <= 0) revert InvalidPrice();
if (block.timestamp - updatedAt > MAX_STALENESS) revert StaleOracle();
return uint256(price);
}7. Proxy / Upgrade Pitfalls
| Risk | Prevention |
| Storage collision | EIP-1967 slots, OZ upgrades plugin |
| Uninitialized proxy | initialize() in same tx as deploy |
| Selector clash | TransparentUpgradeableProxy or UUPS |
| Re-initialization | _disableInitializers() in constructor |
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}8. Signature Replay
error InvalidSignature();
error NonceAlreadyUsed();
mapping(bytes32 => bool) public usedNonces;
function executeWithSig(
address signer, uint256 amount, bytes32 nonce, bytes calldata sig
) external {
if (usedNonces[nonce]) revert NonceAlreadyUsed();
bytes32 digest = keccak256(abi.encodePacked(
"\x19\x01", DOMAIN_SEPARATOR, keccak256(abi.encode(signer, amount, nonce))
));
if (ECDSA.recover(digest, sig) != signer) revert InvalidSignature();
usedNonces[nonce] = true;
}Use EIP-712 typed data + nonce + block.chainid in the domain separator.
Design Patterns
Pull Over Push
// State
mapping(address => uint256) public pending;
// Errors
error NothingToWithdraw();
error TransferFailed();
function recordPayment(address recipient, uint256 amount) internal {
pending[recipient] += amount;
}
function withdraw() external {
uint256 amount = pending[msg.sender];
if (amount == 0) revert NothingToWithdraw();
pending[msg.sender] = 0;
(bool ok, ) = msg.sender.call{value: amount}("");
if (!ok) revert TransferFailed();
}Emergency Stop
import {PausableUpgradeable} from
"@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
contract Protocol is PausableUpgradeable, OwnableUpgradeable {
function deposit() external payable whenNotPaused { /* ... */ }
function pause() external onlyOwner { _pause(); }
function unpause() external onlyOwner { _unpause(); }
}Input Validation
error ZeroAddress();
error ZeroAmount();
error InsufficientBalance(uint256 available, uint256 requested);
function transfer(address to, uint256 amount) external {
if (to == address(0)) revert ZeroAddress();
if (amount == 0) revert ZeroAmount();
if (balances[msg.sender] < amount) {
revert InsufficientBalance(balances[msg.sender], amount);
}
balances[msg.sender] -= amount;
balances[to] += amount;
}Gas Optimization
Never sacrifice correctness for gas. Every unchecked block must have a provable safety invariant.
Storage Packing
// 1 slot (32 bytes)
struct Packed {
uint128 balance;
uint64 lastUpdate;
uint64 nonce;
}Calldata Over Memory
function process(uint256[] calldata data) external pure returns (uint256) {
return data[0];
}Custom Errors Over Revert Strings
Custom errors (Solidity >= 0.8.4) are cheaper than string reverts and encode structured data.
error WithdrawalExceedsBalance(uint256 requested, uint256 available);
function withdraw(uint256 amount) external {
if (amount > address(this).balance) {
revert WithdrawalExceedsBalance(amount, address(this).balance);
}
}Events for Off-Chain Data
event DataStored(address indexed user, uint256 indexed id, bytes data);
function storeData(uint256 id, bytes calldata data) external {
emit DataStored(msg.sender, id, data);
}Only persist to storage what on-chain logic actually reads.
Security Tooling
| Category | Tool | Purpose |
| Static analysis | Slither | Detector suite for common vulns |
| Static analysis | Aderyn | Rust-based, Foundry-native |
| Fuzzing | Echidna | Property-based Solidity fuzzer |
| Fuzzing | Medusa | Go-based alternative to Echidna |
| Formal verification | Certora | Prover for critical invariants |
| Formal verification | Halmos | Symbolic execution for Foundry |
| SMT | SMTChecker | Built-in bounded model checker |
Minimum CI Pipeline
slither . --filter-paths "node_modules|lib"
forge test --fuzz-runs 10000
forge snapshot --checkTesting for Security (Foundry)
import "forge-std/Test.sol";
contract SecurityTest is Test {
Vault vault;
address attacker = makeAddr("attacker");
function setUp() public {
vault = new Vault();
vm.deal(address(vault), 10 ether);
}
function test_RevertWhen_ReentrancyAttempted() public {
ReentrancyAttacker exploit = new ReentrancyAttacker(address(vault));
vm.deal(address(exploit), 1 ether);
vm.expectRevert();
exploit.attack();
}
function test_RevertWhen_UnauthorizedWithdraw() public {
vm.prank(attacker);
vm.expectRevert(Vault.Unauthorized.selector);
vault.emergencyWithdraw();
}
function testFuzz_TransferNeverExceedsBalance(uint256 amount) public {
vm.assume(amount > 0 && amount <= vault.balanceOf(address(this)));
vault.transfer(attacker, amount);
assertEq(vault.balanceOf(attacker), amount);
}
}Audit Preparation
Code Quality
- NatSpec on all public/external functions (
@notice,@dev,@param,@return) - CEI on every state-changing function with external calls
nonReentranton functions sharing mutable stateSafeERC20for all token operations- Custom errors - no revert strings
- No
tx.origin, noblock.timestamprandomness, no on-chain secrets
Testing
- Unit tests: every function, every revert path
- Fuzz tests: property-based for numeric/state edges
- Invariant tests: global properties that must always hold
- Fork tests: integration against mainnet state
- Static analysis clean (Slither + Aderyn, zero high/medium)
Documentation
- Architecture overview with contract interaction diagram
- Threat model: what is trusted, what is adversarial
- Known limitations and design trade-offs
- Deployment and upgrade runbook
- Previous audit reports (if any)
Deployment
- Access control verified and documented
- Upgrade path tested end-to-end (if proxy)
- Testnet deployment validated
- Emergency pause mechanism tested
- Multi-sig or timelock on admin functions
NatSpec Template
/// @title Vault - Collateralized lending vault
/// @notice Accepts collateral deposits and issues vault shares.
/// @dev UUPS-upgradeable. Tiered fee schedule per ADR-018.
contract Vault {
/// @notice Deposit collateral into the vault.
/// @param token Collateral token address.
/// @param amount Deposit amount (must be > 0).
/// @return shares Vault shares minted.
function deposit(address token, uint256 amount) external returns (uint256 shares) {
// ...
}
}Quick Reference
| Vulnerability | Fix |
| Reentrancy | CEI + ReentrancyGuard |
| Missing access control | Ownable2Step / AccessControl |
| Unchecked ERC20 return | SafeERC20 |
| Oracle manipulation | TWAP + freshness check + sanity bounds |
| Frontrunning | Commit-reveal, slippage + deadline params |
| Proxy storage collision | EIP-1967, OZ upgrades plugin |
tx.origin auth | msg.sender |
| On-chain randomness | Chainlink VRF |
| Unbounded loop DoS | Pagination or pull pattern |
| Signature replay | EIP-712 + nonce + block.chainid |
| Flash loan price manipulation | TWAP, multiple oracles |
| Push-payment DoS | Pull-over-push |
| Delegatecall to untrusted | Never; or restrict target via allowlist |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

