---
description: Lint Solidity code with Forge.
---

## Linting

Forge includes a built-in linter to catch common issues and enforce best practices.

### Run the linter

```bash
$ forge lint
```

The linter checks for:
- Incorrect shift operations
- Unchecked external calls
- Divide-before-multiply bugs
- Incorrect ERC721 interface signatures
- Incorrect ERC20 interface definitions
- Strict equality on externally-influenced values
- Unsafe typecasts
- Naming convention violations
- Use of tx.origin for authorization
- Return bomb risks from gas-limited calls
- Unused imports
- Gas optimizations

### Configuration

Configure linter rules in `foundry.toml`:

```toml [foundry.toml]
[lint]
severity = ["high", "med", "low"]
exclude_lints = ["mixed-case-function", "custom-errors"]
```

#### Severity levels

Control which lints run by severity:

```toml [foundry.toml]
[lint]
severity = ["high", "med"]  # Only high and medium severity
```

Valid severity levels: `high`, `med`, `low`, `info`, `gas`, `code-size`

#### Exclude specific lints

Disable specific lint rules globally:

```toml [foundry.toml]
[lint]
exclude_lints = ["mixed-case-variable", "asm-keccak256"]
```

### Ignoring files

Exclude files from linting:

```toml [foundry.toml]
[lint]
ignore = ["src/legacy/**", "test/**"]
```

### Inline suppression

Disable lints for specific lines or blocks using comment directives.

#### Disable on current line

```solidity
uint256 Mixed_Case = 1; // forge-lint: disable-line(mixed-case-variable)
```

#### Disable on next line

```solidity
// forge-lint: disable-next-line(custom-errors)
revert("Use custom errors instead");
```

#### Disable for next item

Disable lints for an entire function, struct, or contract:

```solidity
// forge-lint: disable-next-item(mixed-case-function)
function non_standard_name() public {
    // entire function is excluded from the lint
}
```

#### Disable a block

```solidity
// forge-lint: disable-start(asm-keccak256)
bytes32 hash1 = keccak256(abi.encodePacked(a, b));
bytes32 hash2 = keccak256(abi.encodePacked(c, d));
// forge-lint: disable-end(asm-keccak256)
```

#### Disable multiple lints

```solidity
// forge-lint: disable-next-line(custom-errors, mixed-case-variable)
```

#### Disable all lints

```solidity
// forge-lint: disable-next-line
```

Or explicitly:

```solidity
// forge-lint: disable-next-line(all)
```

### Disable linting on build

By default, `forge build` runs the linter. To disable for a single invocation, pass
`--no-lint` (alias `--skip-lint`):

```bash
forge build --no-lint
```

To disable persistently:

```toml [foundry.toml]
[lint]
lint_on_build = false
```

### CI integration

Add linting to your CI pipeline with `--deny warnings`, which exits with a non-zero
status when warning-level lint diagnostics are emitted:

```yaml
- name: Run linter
  run: forge lint --deny warnings
```

See the [linter reference](/config/reference/linter) for all configuration options.
### Lint reference

Every lint emitted by `forge lint` has its own page describing what it flags, why it
matters, and how to fix it. Use the index below to jump to a specific lint, or use the
navigation on the right to browse by severity.

#### High severity

- [`arbitrary-send-erc20`](/forge/linting/arbitrary-send-erc20) — Flags `transferFrom` / `safeTransferFrom` calls whose `from` argument is not provably equal to `msg.sender` or `address(this)`.
- [`arbitrary-send-erc20-permit`](/forge/linting/arbitrary-send-erc20-permit) — Flags `transferFrom` calls with an arbitrary `from` after a `permit(owner, address(this), …)` on the same `(token, owner)`.
- [`arbitrary-send-eth`](/forge/linting/arbitrary-send-eth) — Flags functions that send ETH (via `transfer` / `send` / `{value:}` calls / `selfdestruct` / known OZ + Solady helpers) to a caller-controlled destination without restricting who can call them.
- [`controlled-delegatecall`](/forge/linting/controlled-delegatecall) — Flags `delegatecall` targets that are not provably trusted.
- [`encode-packed-collision`](/forge/linting/encode-packed-collision) — Flags `abi.encodePacked()` calls with two or more dynamic-type arguments (`string`, `bytes`, `T[]`) that can produce hash collisions.
- [`enumerable-loop-removal`](/forge/linting/enumerable-loop-removal) — Flags `remove` on an EnumerableSet inside a loop that also iterates a set with `at`; swap-and-pop removal corrupts the iteration.
- [`erc20-unchecked-transfer`](/forge/linting/erc20-unchecked-transfer) — Flags calls to ERC20 `transfer` and `transferFrom` where the boolean return value is ignored.
- [`function-selector-collision`](/forge/linting/function-selector-collision) — Flags different proxy and implementation function signatures that produce the same four-byte selector.
- [`incorrect-exp`](/forge/linting/incorrect-exp) — Flags `^` (bitwise xor) used between integer literals where `**` (exponentiation) was almost certainly intended.
- [`incorrect-shift`](/forge/linting/incorrect-shift) — Flags shift operations where a literal appears on the left and a non-literal on the right, which is almost always the wrong operand order.
- [`protected-vars`](/forge/linting/protected-vars) — Flags externally callable functions that can write an annotated state variable without invoking its declared protection.
- [`reentrancy-balance`](/forge/linting/reentrancy-balance) — Flags reentrant external calls between saving the current contract balance and checking a fresh balance against that stale value.
- [`reentrancy-eth`](/forge/linting/reentrancy-eth) — Flags uncapped ETH-transferring low-level `call` operations when state read before the call is written after the call.
- [`rtlo`](/forge/linting/rtlo) — Flags the presence of Unicode bidirectional override characters in source code, which can be used to hide malicious behavior ("Trojan Source", [CVE-2021-42574](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-42574)).
- [`unchecked-call`](/forge/linting/unchecked-call) — Flags low-level calls (`call`, `delegatecall`, `staticcall`, `callcode`) whose `success` return value is ignored.
- [`unprotected-initializer`](/forge/linting/unprotected-initializer) — Flags upgradeable initializers that can be called directly on an implementation contract with a destructive entry point.

#### Medium severity

- [`assert-state-change`](/forge/linting/assert-state-change) — Flags expressions inside `assert()` that modify contract state.
- [`boolean-cst`](/forge/linting/boolean-cst) — Flags expressions where a boolean constant (`true`/`false`) is used as a control-flow condition or operand of a boolean operator, which usually indicates dead code or a leftover debug toggle.
- [`dangerous-unary-operator`](/forge/linting/dangerous-unary-operator) — Flags assignments whose `=` is fused to a unary operator (`=-`, `=~`), which parses as a plain assignment rather than the compound assignment it resembles.
- [`divide-before-multiply`](/forge/linting/divide-before-multiply) — Flags arithmetic expressions where division is performed before multiplication, which can cause unintended precision loss in integer arithmetic.
- [`ecrecover`](/forge/linting/ecrecover) — Flags direct calls to Solidity's `ecrecover` builtin when the signature's `s` value is not proven to be canonical.
- [`incorrect-erc20-interface`](/forge/linting/incorrect-erc20-interface) — Flags interfaces or contracts whose function signatures match an ERC20 method by name and parameters but use the wrong return type.
- [`incorrect-erc721-interface`](/forge/linting/incorrect-erc721-interface) — Flags interfaces or contracts whose function signatures match an ERC721 (or ERC165) method by name and parameters but use the wrong return type.
- [`locked-ether`](/forge/linting/locked-ether) — Flags contracts that can receive Ether (via `payable` functions, `receive()`, or a payable `fallback()`) but expose no code path that can send Ether out, permanently trapping user funds.
- [`low-level-calls`](/forge/linting/low-level-calls) — Flags direct use of Solidity low-level calls (`call`, `delegatecall`, and `staticcall`).
- [`mapping-deletion`](/forge/linting/mapping-deletion) — Flags `delete` applied to a value whose type contains a `mapping`, because mapping entries are left in storage.
- [`non-reentrant-not-first`](/forge/linting/non-reentrant-not-first) — Flags functions where a `nonReentrant` modifier appears after another modifier.
- [`reentrancy-no-eth`](/forge/linting/reentrancy-no-eth) — Flags non-ETH external calls when state read before the call is written after the call.
- [`tautological-compare`](/forge/linting/tautological-compare) — Flags relational or equality comparisons whose two sides are the same side-effect-free expression.
- [`tx-origin`](/forge/linting/tx-origin) — Flags use of `tx.origin` inside authorization-like predicates, which can be exploited by a malicious contract acting on behalf of a legitimate owner.
- [`type-based-tautology`](/forge/linting/type-based-tautology) — Flags comparison expressions that are always true or always false due to the numeric range of the variable's type.
- [`uninitialized-local`](/forge/linting/uninitialized-local) — Flags local variables that are declared without an initializer and read before any explicit assignment, where the silent zero-default is almost certainly unintentional.
- [`uninitialized-state`](/forge/linting/uninitialized-state) — Flags state variables that are read in a contract's inheritance chain but never assigned, leaving them silently equal to their type's zero default.
- [`unsafe-oz-erc721-mint`](/forge/linting/unsafe-oz-erc721-mint) — Flags calls resolving to `ERC721._mint`, which does not check that the recipient can receive the token; use `_safeMint`.
- [`unsafe-typecast`](/forge/linting/unsafe-typecast) — Flags explicit numeric typecasts that can silently truncate or alter the value.
- [`unused-return`](/forge/linting/unused-return) — Flags external calls whose return value is silently discarded.
- [`weak-prng`](/forge/linting/weak-prng) — Flags randomness-like expressions that directly derive entropy from predictable on-chain values.

#### Low severity

- [`block-timestamp`](/forge/linting/block-timestamp) — Flags use of `block.timestamp` as an operand of a comparison, where its value can be slightly manipulated by the block proposer.
- [`calls-loop`](/forge/linting/calls-loop) — Flags external calls made from inside loops, which can cause denial-of-service if a callee reverts or exhausts gas.
- [`delegatecall-loop`](/forge/linting/delegatecall-loop) — Flags `delegatecall` operations inside loops in externally callable payable functions.
- [`deprecated-oz-function`](/forge/linting/deprecated-oz-function) — Flags references to functions OpenZeppelin deprecated: `SafeERC20.safeApprove` and `AccessControl._setupRole`.
- [`empty-block`](/forge/linting/empty-block) — Flags regular functions with an empty body; constructors, `receive`/`fallback`, `virtual` and `payable` functions are exempt.
- [`incorrect-modifier`](/forge/linting/incorrect-modifier) — Flags modifiers that can finish without executing the modified function body or reverting.
- [`inconsistent-type-names`](/forge/linting/inconsistent-type-names) — Flags mixed use of `uint`/`uint256` or `int`/`int256` spellings within one contract.
- [`missing-events-access-control`](/forge/linting/missing-events-access-control) — Flags protected entry-point functions that update state used for access control without emitting an event.
- [`missing-events-arithmetic`](/forge/linting/missing-events-arithmetic) — Flags protected entry-point functions that update tainted integer state used in arithmetic by an unprotected function without emitting an event.
- [`missing-zero-check`](/forge/linting/missing-zero-check) — Flags entry-point functions and constructors where an `address` parameter flows into a state write or value transfer without a zero-address guard.
- [`msg-value-loop`](/forge/linting/msg-value-loop) — Flags `msg.value` reads inside loops reachable from externally callable payable functions.
- [`reentrancy-events`](/forge/linting/reentrancy-events) — Flags `emit` statements reachable after an external call, where a reentrant callee can reorder or fabricate logs that off-chain consumers rely on.
- [`require-revert-in-loop`](/forge/linting/require-revert-in-loop) — Flags `require` calls and `revert` statements that execute inside loops, including checks reached through internal helpers.
- [`return-bomb`](/forge/linting/return-bomb) — Flags external calls that set an explicit gas limit while copying unbounded dynamic returndata.
- [`solmate-safe-transfer-lib`](/forge/linting/solmate-safe-transfer-lib) — Flags solmate `SafeTransferLib` token operations: the released library does not check that the token has code.

#### Informational

- [`boolean-equal`](/forge/linting/boolean-equal) — Flags expressions of the form `x == true`, `x == false`, `x != true`, `x != false`, which can be simplified.
- [`event-fields`](/forge/linting/event-fields) — Flags events whose `address` parameters are not declared `indexed`.
- [`function-init-state`](/forge/linting/function-init-state) — Flags state variables whose initializer depends on a non-pure function or another state variable; initializers run before the constructor body.
- [`inline-assembly`](/forge/linting/inline-assembly) — Flags every `assembly { ... }` block; inline assembly bypasses Solidity safety features and should be reviewed deliberately.
- [`interface-file-naming`](/forge/linting/interface-file-naming) — Flags Solidity files whose only top-level declaration is an interface but whose filename is not prefixed with `I`.
- [`interface-naming`](/forge/linting/interface-naming) — Flags `interface` declarations whose names are not prefixed with `I`.
- [`missing-inheritance`](/forge/linting/missing-inheritance) — Flags contracts that implement every external function of an interface but do not explicitly inherit from it.
- [`mixed-case-function`](/forge/linting/mixed-case-function) — Flags function names that do not follow `mixedCase`.
- [`mixed-case-variable`](/forge/linting/mixed-case-variable) — Flags mutable variable names (locals, parameters, mutable state) that do not follow `mixedCase`.
- [`multi-contract-file`](/forge/linting/multi-contract-file) — Flags source files that declare more than one top-level contract, interface, or library.
- [`named-struct-fields`](/forge/linting/named-struct-fields) — Flags struct construction expressions that pass fields positionally instead of by name.
- [`pascal-case-struct`](/forge/linting/pascal-case-struct) — Flags struct definitions whose names do not follow `PascalCase`.
- [`pragma-inconsistent`](/forge/linting/pragma-inconsistent) — Flags projects whose source files declare incompatible or differently-shaped Solidity version pragmas.
- [`redundant-base-constructor-call`](/forge/linting/redundant-base-constructor-call) — Flags explicit empty base-constructor arguments (e.g. `is A()` or `constructor() A() {}`) when the base contract requires no arguments.
- [`reentrancy-unlimited-gas`](/forge/linting/reentrancy-unlimited-gas) — Flags state changes and event emissions that remain reachable after built-in `transfer` or `send` calls.
- [`screaming-snake-case-const`](/forge/linting/screaming-snake-case-const) — Flags `constant` state variables whose names do not follow `SCREAMING_SNAKE_CASE`.
- [`screaming-snake-case-immutable`](/forge/linting/screaming-snake-case-immutable) — Flags `immutable` state variables whose names do not follow `SCREAMING_SNAKE_CASE`.
- [`too-many-digits`](/forge/linting/too-many-digits) — Flags numeric literals containing five or more consecutive zeros, which are easy to misread.
- [`unaliased-plain-import`](/forge/linting/unaliased-plain-import) — Flags `import "path";` statements that pull in every top-level symbol from another file without an alias.
- [`unsafe-cheatcode`](/forge/linting/unsafe-cheatcode) — Flags use of Foundry cheatcodes that perform dangerous side effects (filesystem access, network activity, environment variable reads, etc.) so they cannot slip into production code unnoticed.
- [`literal-instead-of-constant`](/forge/linting/literal-instead-of-constant) — Flags number, address and hex string literals repeated inside a contract; declare a named constant instead.
- [`modifier-used-only-once`](/forge/linting/modifier-used-only-once) — Flags modifiers invoked by exactly one function; their checks can usually be inlined.
- [`internal-function-used-once`](/forge/linting/internal-function-used-once) — Flags internal functions referenced exactly once; they can usually be inlined into their caller.
- [`cyclomatic-complexity`](/forge/linting/cyclomatic-complexity) — Flags functions whose cyclomatic complexity is strictly above 11; split them into smaller functions.
- [`incorrect-using-for`](/forge/linting/incorrect-using-for) — Flags `using ... for` directives naming a library with no function applicable to the type: they attach nothing.
- [`unused-error`](/forge/linting/unused-error) — Flags custom error declarations that are never referenced anywhere in the compiled sources.
- [`unused-import`](/forge/linting/unused-import) — Flags imported symbols (or whole import statements) whose imported names are not referenced anywhere in the source unit.
- [`todo-comment`](/forge/linting/todo-comment) — Flags `TODO` and `FIXME` markers left in comments, which signal unfinished work or known bugs that have not been resolved before the code reached production.

#### Gas optimization

- [`asm-keccak256`](/forge/linting/asm-keccak256) — Flags calls to the high-level `keccak256(...)` builtin that can be cheaply rewritten with inline assembly.
- [`cache-array-length`](/forge/linting/cache-array-length) — Flags `for` loop conditions that read a storage dynamic array's `.length` on every iteration instead of comparing against a cached local length.
- [`costly-loop`](/forge/linting/costly-loop) — Flags storage variable writes inside loops, where each SSTORE multiplies gas cost by the number of iterations.
- [`could-be-constant`](/forge/linting/could-be-constant) — Flags state variables with a compile-time-constant inline initializer that are never written — making them eligible to be declared `constant`.
- [`could-be-immutable`](/forge/linting/could-be-immutable) — Flags state variables that are assigned only in the constructor and never written to afterward — making them eligible to be declared `immutable`.
- [`custom-errors`](/forge/linting/custom-errors) — Flags `require(cond, "message")`, `revert("message")`, and `revert()` calls; suggests replacing them with a `revert CustomError(...)`.
- [`external-function`](/forge/linting/external-function) — Flags `public` functions that are never called internally and have a reference-type `memory` parameter; switching them to `external` (with `calldata` parameters) avoids an extra calldata-to-memory copy.
- [`unused-state-variables`](/forge/linting/unused-state-variables) — Flags state variables that are declared but never read or written anywhere in the contract or its descendants.
- [`var-read-using-this`](/forge/linting/var-read-using-this) — Flags `this.X(...)` calls that read the contract's own state through its external interface; the unnecessary `STATICCALL` can be avoided by accessing the variable directly.
- [`write-after-write`](/forge/linting/write-after-write) — Flags storage variables written consecutively without the first value being read; only the final write is needed.

#### Code size

- [`unwrapped-modifier-logic`](/forge/linting/unwrapped-modifier-logic) — Flags modifiers whose body contains non-trivial logic that should be moved into a helper function to reduce contract code size.
