Skip to main content

solidity-coding

Write and modify production Solidity contracts in Foundry projects. Use when writing a contract, implementing a function, adding errors, events, or structs, designing contract architecture, optimizing gas, hardening security, planning upgrades, or working in src/.

Quellinformationen

Repository
PaulRBerg/foundry-template
Letzte Quellaktivität
24. September 2026 um 11:27
Erkannte Sprache von SKILL.md
Englisch
Sterne
642
Forks
131

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
7 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
solidity-coding
description
Write and modify production Solidity contracts in Foundry projects. Use when writing a contract, implementing a function, adding errors, events, or structs, designing contract architecture, optimizing gas, hardening security, planning upgrades, or working in src/.
# Solidity Coding Write Solidity that compiles under the project's pinned toolchain, follows its conventions, and passes its lint gate. ## Ground Rules - Read `foundry.toml`, `remappings.txt`, and the nearest `AGENTS.md` before writing code. The pinned `solc` and the effective EVM version decide which features exist: transient storage and `mcopy` need Cancun or later, and `transient` state variables also need solc 0.8.28 or later. When `evm_version` is unset, Foundry's default applies; read it with `forge config | grep evm_version`. - Treat the defaults below as fallbacks. Existing code and repository instructions win; match the surrounding file when they disagree. - Let `forge fmt`, Solhint, and Prettier own formatting. Run them instead of hand-formatting. ## References - [references/security-practices.md](references/security-practices.md): read when a change touches access control, external calls, hooks, `unchecked` arithmetic, token integrations, signatures, oracles, or account abstraction. - [references/gas-optimization.md](references/gas-optimization.md): read when optimizing gas, packing storage, using transient storage, or choosing between OpenZeppelin and Solady. - [references/event-design.md](references/event-design.md): read when adding or changing events, especially ones that indexers consume. - [references/upgrades-and-versioning.md](references/upgrades-and-versioning.md): read when writing upgradeable contracts, changing storage layout, or shipping a new version beside a deployed one. - [references/onchain-metadata.md](references/onchain-metadata.md): read when implementing `tokenURI` or onchain JSON/SVG metadata. ## Conventions ### Naming | Element | Convention | Example | | --------------------------------------- | -------------------------- | ---------------------- | | Contract, library, struct, enum, event | PascalCase | `TokenVault` | | Interface | `I` + PascalCase | `ITokenVault` | | Function, variable, parameter | camelCase | `withdrawableAmountOf` | | Constant, immutable | SCREAMING_SNAKE_CASE | `MAX_FEE` | | Internal or private function, state var | Leading underscore | `_balances`, `_update` | | Custom error | `{Contract}_{Description}` | `TokenVault_Overdraw` | | Type namespace library | Domain name | `Vault.Position` | ### Imports 1. Use named imports only: `import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";`. 2. Put remapped package imports first, then a blank line, then relative local imports. 3. Sort each group alphabetically by path. ### Layout Grow a larger project into this shape; keep a small one flat until a directory has content: ``` src/ ├── TokenVault.sol # Entry-point contracts ├── abstracts/ # Base contracts in the inheritance chain (state, shared logic) ├── interfaces/ # Public APIs; the full NatSpec lives here ├── libraries/ # Errors.sol, math and helper libraries └── types/ # Structs, enums, and namespace libraries (e.g. DataTypes.sol) ``` ### Inheritance List parents alphabetically when C3 linearization allows it. When one parent derives from another, list the most base-like first, or the compiler rejects the linearization. ### Sections and Ordering Group contract members under section headers, omitting empty sections, in this order: 1. STATE VARIABLES 2. CONSTRUCTOR 3. MODIFIERS 4. USER-FACING READ-ONLY FUNCTIONS 5. USER-FACING STATE-CHANGING FUNCTIONS 6. INTERNAL READ-ONLY FUNCTIONS 7. INTERNAL STATE-CHANGING FUNCTIONS 8. PRIVATE READ-ONLY FUNCTIONS 9. PRIVATE STATE-CHANGING FUNCTIONS Headers use this shape; the [`headers`](https://github.com/transmissions11/headers) CLI generates them when installed (`headers "CONSTRUCTOR"`): ```solidity /*////////////////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////////////////*/ ``` Order signature keywords as visibility, mutability, `virtual`, `override`, then custom modifiers, with guards such as `nonReentrant` first. ### NatSpec Put full NatSpec in the interface and use `/// @inheritdoc IContract` in the implementation. Document every public and external function in this order, separating blocks with `///` lines: ```solidity /// @notice Withdraws `amount` from the position to `to`. /// /// @dev Emits a {Withdraw} event. /// /// Notes: /// - Anyone may call this function, but only the position owner receives the tokens. /// /// Requirements: /// - `amount` must not exceed the withdrawable amount. /// /// @param positionId The ID of the position to withdraw from. /// @param to The address receiving the tokens. /// @param amount The amount to withdraw, denoted in units of the token's decimals. /// @return withdrawnAmount The amount withdrawn. ``` Document structs with `@param` for each field and errors with `/// @notice Thrown when ...`. ### Errors - Use custom errors, never revert strings. - Define one error per failure mode; never merge conditions that deserve different errors into one check. - Include parameters that help debugging, such as the requested and the available value. - Multi-contract projects keep errors in `src/libraries/Errors.sol` (`library Errors`), grouped under one section per contract, and revert with `revert Errors.TokenVault_Overdraw(...)`. Single-contract projects may declare errors in the interface. ```solidity /// @notice Thrown when trying to withdraw more than the withdrawable amount. error TokenVault_Overdraw(uint256 positionId, uint128 amount, uint128 withdrawableAmount); ``` ### Checks-Effects-Interactions 1. **Checks**: validate inputs and state; revert on failure. 2. **Effects**: write all state before any external call. 3. **Interactions**: make external calls (token transfers, hooks) last. 4. **Protocol invariant** (optional): `assert` a post-interaction invariant only when it is cheap to verify (the FREI-PI pattern). Emit events after the state changes they describe. ### Tokens and Low-Level Calls - Move ERC-20 tokens through OpenZeppelin `SafeERC20` (`safeTransfer`, `safeTransferFrom`, `forceApprove`). - Use low-level calls only for targets that may not implement the expected interface; check `success` and `returndata.length` before decoding. - Prefer `memory` copies for repeated reads of a struct and `storage` references for writes. ### Types Default to `uint256`. Use smaller types only where they pack into a shared slot, such as `uint128` amounts and `uint40` timestamps. Standalone small types cost extra gas for masking. ## Common Tasks - **Add a function**: declare it with full NatSpec in the interface, implement it with `@inheritdoc` in the matching section, apply guard modifiers, follow CEI, and emit an event for every state change. - **Add an error**: follow [Errors](#errors) and add a test that expects its selector and arguments. - **Add a struct**: define it under `types/` (or the interface for small projects), order fields to pack slots, and document each field. - **Add an event**: read [references/event-design.md](references/event-design.md) first. ## Stack Too Deep Bundle locals into a memory struct named `{FunctionName}Vars`, defined in `types/` when shared or next to the function otherwise: ```solidity /// @dev Needed to avoid Stack Too Deep. struct TokenURIVars { address token; uint128 depositedAmount; string symbol; } ``` Enabling `via_ir = true` also resolves most cases, but it changes the bytecode and slows compilation; make it a deliberate project-wide choice, not a local fix. ## Contract Size Deployed bytecode must stay under the EIP-170 limit of 24,576 bytes. Check with `forge build --sizes` under the production profile. When a contract exceeds it: 1. Move logic into `external`/`public` library functions, which are called via `DELEGATECALL` instead of being inlined like `internal` ones; each call costs slightly more gas. 2. Split responsibilities across contracts, such as a separate descriptor or periphery contract. 3. Route repeated modifier bodies through private functions. 4. Remove dead code and lower `optimizer_runs` for rarely called contracts. ## Completion Finish only when: - `forge build` succeeds, plus `forge build --sizes` when the change could affect size. - The repository's lint gate passes for touched files; in this template that is `forge fmt --check <files>` and `bun solhint <files>` (or `bun run full-check`). - New behavior has tests, or the report names the missing coverage. Report the exact commands and results, and any convention or checklist item left open.
Auf GitHub ansehen