Use when designing, implementing, or reviewing Solidity contracts that hold assets, account for shares or debt, read price oracles, verify signatures, integrate ERC-20s, or sit behind upgradeable proxies, and for a pre-deployment security review. Not for test-suite design (`/testing`) or a full audit report (`/audit`).
Installs into .claude/skills of the current project.
Sponsors
SSkills DirectorySkills Directory
Is that skill safe to install?
SSkills DirectorySkills Directory
Know which skills are safe — weekly.
Are you the author of Austintgriffith Official Security?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/jiayaoqijia-austintgriffith-official-security)
---
name: security
description: "Use when designing, implementing, or reviewing Solidity contracts that hold assets, account for shares or debt, read price oracles, verify signatures, integrate ERC-20s, or sit behind upgradeable proxies, and for a pre-deployment security review. Not for test-suite design (`/testing`) or a full audit report (`/audit`)."
---
# Smart Contract Security
Treat every external token, price, signature, callback, and privileged caller as adversarial. Prefer audited OpenZeppelin primitives over hand-rolled implementations, but verify the installed library version and the assumptions around each primitive.
## Asset accounting
- Keep units explicit. ERC-20 decimals and oracle decimals vary; normalize every quantity to one documented scale before comparing collateral, debt, fees, or shares. Multiply before dividing and use full-precision `mulDiv` where intermediate overflow or rounding matters.
- For arbitrary ERC-20s, use `SafeERC20.safeTransfer` and `safeTransferFrom`. Credit deposits from the contract's balance delta, not the requested amount, or reject tokens whose received amount differs. Fee-on-transfer and rebasing tokens otherwise break custody accounting.
- On OpenZeppelin Contracts v5, use `SafeERC20.forceApprove` when an allowance may already be nonzero. `safeApprove` was removed. Keep approvals exact or bounded and clear leftovers when practical.
- Tokens can freeze, blocklist, or pause (USDC and USDT do). One blocked recipient makes a transfer revert, so a loop that pushes payments to many users bricks for all of them. Credit balances and let each user withdraw (pull payments).
- Apply checks-effects-interactions and a reentrancy guard to value-moving entry points. Update shares, debt, and other accounting before external calls.
For a vault or receipt-token design, start from OpenZeppelin `ERC4626`. A hand-rolled vault must defend its empty state against first-depositor donation/inflation attacks with virtual assets and shares or another explicit mitigation. Test direct donations and rounding at small deposits; never use an unprotected `assets * totalSupply / totalAssets` conversion with a special 1:1 empty-vault path.
## Prices and liquidations
Never use a DEX spot read (`slot0`, reserves, balances, or a current quote) for collateral, borrowing, minting, or liquidation decisions. Pool depth does not fix atomic manipulation: flash-borrowed capital can move the price, exploit the dependent action, unwind, and repay inside one transaction before arbitrage can intervene.
Use a manipulation-resistant push feed for high-value decisions, or a meaningful TWAP when its weaker guarantees fit the risk. For every feed:
- require a positive answer;
- validate `updatedAt` against a per-feed maximum age derived from that feed's published heartbeat plus a justified margin—never one global hardcoded timeout;
- handle feed and token decimals explicitly;
- define behavior for sequencer downtime where relevant; and
- bound liquidation incentives and any swap's minimum output.
## Swaps
Every swap a contract or a user makes needs a real minimum output, derived from an oracle or an offchain quote minus a stated slippage tolerance. `amountOutMinimum = 0` (or a minimum computed from the same pool's spot price) hands the trade to any sandwich bot. Pass a real `deadline`, not `block.timestamp`, so a held transaction can't execute later at a worse price. For large user swaps, offer private routing (e.g. Flashbots Protect) or a batch auction. On an L2 the sequencer orders transactions, which narrows but does not remove this risk; keep the minimum output either way.
## Signatures and replay protection
Use EIP-712 and OpenZeppelin's implementation rather than assembling signature recovery from scratch. Bind the signed action to the intended contract and chain, consume a signer-scoped nonce, enforce a deadline, and update replay state before moving assets.
If a domain separator is cached, also cache the deployment chain id and re-derive the separator whenever `block.chainid` differs. An immutable separator computed only at deployment becomes stale after a fork. OpenZeppelin `EIP712` handles this invalidation.
## Upgradeability and authority
Use an upgradeable design only when post-deployment changes are required. With proxies:
- initialize through an initializer and disable initializers on the implementation;
- on UUPS, `_authorizeUpgrade` must carry access control (`onlyOwner` or a role). Left empty, anyone can upgrade the proxy to their own code and drain it;
- use the upgradeable variants of inherited contracts;
- preserve inheritance order and storage layout—append variables only; never insert, delete, reorder, or change an existing type;
- run the framework's storage-layout compatibility check before every upgrade; and
- put upgrade and emergency authority behind the intended multisig or timelock, not a single EOA.
Every privileged state change needs explicit access control. Make pause, upgrade, rescue, oracle-update, and parameter-update powers visible in the threat model; a safety switch controlled by one key is also a censorship and compromise risk. Never `delegatecall` to a user-selected target.
## Before deployment
- Trace assets, shares, debt, approvals, callbacks, and privilege through every success and failure path.
- Test zero, maximum, rounding, donation, fee-on-transfer, stale-price, replay, reentrancy, and unauthorized-caller cases that apply.
- Run unit, fuzz, invariant, and pinned-fork tests appropriate to the integrations; use the `testing` skill for suite design.
- Run static analysis and resolve every high or medium finding or document why it is a false positive.
- Verify deployed bytecode and transfer production authority to the intended multisig or timelock.
- For a comprehensive pre-deployment review across domains, use the `audit` skill.
Before you finish: send a one-line note via [feedback/SKILL.md](https://ethskills.com/feedback/SKILL.md) — issue or praise.