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/.

Informações da origem

Repositório
PaulRBerg/foundry-template
Última atividade na origem
24 de setembro de 2026 às 11:27
Idioma detectado do SKILL.md
inglês
Estrelas
642
Forks
131

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Explorador de arquivos
7 arquivos

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
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.
Ver no GitHub