solidity-security
Scannednpx machina-cli add skill 0xlayerghost/solidity-agent-kit/solidity-security --openclawSolidity Security Standards
Language Rule
- Always respond in the same language the user is using. If the user asks in Chinese, respond in Chinese. If in English, respond in English.
Private Key Protection
- Store private keys in
.env, load viasource .env— never pass keys as CLI arguments - Never expose private keys in logs, screenshots, conversations, or commits
- Provide
.env.examplewith placeholder values for team reference - Add
.envto.gitignore— verify withgit statusbefore every commit
Security Decision Rules
When writing or reviewing Solidity code, apply these rules:
| Situation | Required Action |
|---|---|
| External ETH/token transfer | Use ReentrancyGuard + Checks-Effects-Interactions (CEI) pattern |
| ERC20 token interaction | Use SafeERC20 — call safeTransfer / safeTransferFrom, never raw transfer / transferFrom |
| Owner-only function | Inherit Ownable2Step (preferred) or Ownable from OZ 4.9.x — Ownable2Step prevents accidental owner loss |
| Multi-role access | Use AccessControl from @openzeppelin/contracts/access/AccessControl.sol |
| Token approval | Use safeIncreaseAllowance / safeDecreaseAllowance from SafeERC20 — never raw approve |
| Price data needed | Use Chainlink AggregatorV3Interface if feed exists; otherwise TWAP with min-liquidity check — never use spot pool price directly |
| Upgradeable contract | Prefer UUPS (UUPSUpgradeable) over TransparentProxy; always use Initializable |
| Solidity version < 0.8.0 | Must use SafeMath — but strongly prefer upgrading to 0.8.20+ |
| Emergency scenario | Inherit Pausable, add whenNotPaused to user-facing functions; keep admin/emergency functions unpaused |
| Whitelist / airdrop | Use MerkleProof for gas-efficient verification — never store full address lists on-chain |
| Signature-based auth | Use ECDSA + EIP712 — never roll custom signature verification |
| Signature content | Signature must bind chainId + nonce + msg.sender + deadline — prevent replay and cross-chain reuse |
| Cross-chain bridge / third-party dependency | Audit all inherited third-party contract code — never assume dependencies are safe |
| Deprecated / legacy contracts | Permanently pause or selfdestruct deprecated contracts — never leave unused contracts callable on-chain |
| UUPS upgrade pattern | _authorizeUpgrade must have onlyOwner; implementation constructor calls _disableInitializers(); retain onlyProxy on upgradeTo — EVMbench/basin H-01 |
| Multi-contract trust boundary | Router/Registry relay calls must verify source contract authorization; never trust caller identity inside flash loan callbacks — EVMbench/noya H-08 |
| Counter/ID + external call | All counter increments and ID assignments must complete before external calls; ETH refunds must be last — EVMbench/phi H-06 |
Reentrancy Protection
- All contracts with external calls: inherit
ReentrancyGuard, addnonReentrantmodifier- Import:
@openzeppelin/contracts/security/ReentrancyGuard.sol(OZ 4.9.x)
- Import:
- Always apply CEI pattern even with
ReentrancyGuard:- Checks — validate all conditions (
require) - Effects — update state variables
- Interactions — external calls last
- Checks — validate all conditions (
Input Validation
- Reject
address(0)for all address parameters - Reject zero amounts for fund transfers
- Validate array lengths match when processing paired arrays
- Bound numeric inputs to reasonable ranges (prevent dust attacks, gas griefing)
Gas Control
- Deployment commands must include
--gas-limit(recommended >= 3,000,000) - Monitor gas with
forge test --gas-report— review before every PR - Configure optimizer in
foundry.toml:optimizer = true,optimizer_runs = 200 - Avoid unbounded loops over dynamic arrays — use pagination or pull patterns
Pre-Audit Checklist
Before submitting code for review or audit, verify:
Access & Control:
- All external/public functions have
nonReentrantwhere applicable - No
tx.originused for authentication (usemsg.sender) - No
delegatecallto untrusted addresses - Owner transfer uses
Ownable2Step(notOwnable) to prevent accidental loss - Contracts with user-facing functions inherit
Pausablewithpause()/unpause() - UUPS
_authorizeUpgradehasonlyOwnermodifier — EVMbench/basin H-01 - Implementation constructor calls
_disableInitializers()— EVMbench/basin H-01 - Router/Registry relay operations verify source contract authorization — EVMbench/noya H-08
Token & Fund Safety:
- All ERC20 interactions use
SafeERC20(safeTransfer/safeTransferFrom) - No raw
token.transfer()orrequire(token.transfer())patterns - Token approvals use
safeIncreaseAllowance, not rawapprove - All
external callreturn values checked
Code Quality:
- Events emitted for every state change
- No hardcoded addresses — use config or constructor params
-
.envis in.gitignore
Oracle & Price (if applicable):
- Price data sourced from Chainlink feed or TWAP — never raw spot price
- Oracle has minimum liquidity check — revert if pool reserves too low
- Price deviation circuit breaker in place
Testing:
-
forge testpasses with zero failures -
forge coverageshows adequate coverage on security-critical paths - Fuzz tests cover arithmetic edge cases (zero, max uint, boundary values)
Security Verification Commands
# Run all tests with gas report
forge test --gas-report
# Fuzz testing with higher runs for critical functions
forge test --fuzz-runs 10000
# Check test coverage
forge coverage
# Dry-run deployment to verify no runtime errors
forge script script/Deploy.s.sol --fork-url $RPC_URL -vvvv
# Static analysis (if slither installed)
slither src/
Source
git clone https://github.com/0xlayerghost/solidity-agent-kit/blob/main/skills/solidity-security/SKILL.mdView on GitHub Overview
Solidity Security Standards establish a pre-coding safety net for Solidity contracts. It enforces private key protection, robust access control, and reentrancy prevention, while providing pre-audit checklists to catch issues early.
How This Skill Works
Apply the security decision rules during design and review: store keys in .env, avoid exposing keys, and implement patterns like ReentrancyGuard, Ownable2Step, and AccessControl. Use SafeERC20 for token transfers, proper price data handling, and upgradeability considerations, plus input validation.
When to Use It
- Creating or editing any Solidity contract (.sol) from scratch or during refactors.
- Reviewing a pull request that includes changes to Solidity source files.
- Implementing external transfers, token interactions, or access control patterns in a contract.
- Auditing upgradeable contracts or multi-contract systems with trust boundaries.
- Setting up CI/CD deployment where private keys or environment secrets must be protected.
Quick Start
- Step 1: Create a .env with private keys and run 'source .env' to load securely; ensure .env is gitignored.
- Step 2: Add ReentrancyGuard, CEI pattern, and SafeERC20 usage to your .sol files where external calls or token interactions occur.
- Step 3: Run the pre-audit checklist and tests before deployment to validate access control, key handling, and safety checks.
Best Practices
- Store private keys in a .env file and load them with 'source .env'—never pass keys as CLI arguments.
- Inherit ReentrancyGuard and apply the nonReentrant modifier on contracts with external calls, following the CEI (Checks-Effects-Interactions) pattern.
- Prefer Ownable2Step (or OZ Ownable) for owner-only functions to prevent accidental ownership loss.
- Use SafeERC20 and safeTransfer/safeTransferFrom for all ERC20 interactions; avoid raw transfer/transferFrom.
- Audit all inherited third-party contracts and ensure price feeds (Chainlink or TWAP) are used when needed; avoid on-chain price assumptions.
Example Use Cases
- A payment contract defends against reentrancy by applying nonReentrant and CEI around external calls.
- An ERC20-based sale uses SafeERC20's safeTransfer and safeTransferFrom for all transfers.
- Owner-only functions are guarded with Ownable2Step to prevent accidental ownership loss.
- A multi-role system employs AccessControl to manage permissions across roles.
- A token drop uses MerkleProof to verify whitelisted addresses without storing full on-chain lists.