# Shadowed state writes silently lost

Writes to memory copies or shadowing locals that never reach storage, leaving stale state

_Area: Smart contract access control and control flow. Seen in 9 disclosed reports; latest 2026-02-05. Updated 2026-10-23._

## How it works
Solidity copies a storage struct into a `memory` local, or a local declaration reuses a state variable's name. Mutations then hit the copy or the shadow, and storage keeps its old value. The code reads as if it updated state; the compiler accepts it; nothing persists. The broken assumption is that assigning from a mapping or state variable yields a reference. It yields a copy unless the qualifier is `storage`.

## Where it shows up
- Internal setters or helpers taking a struct as `memory` (or returning `Edge memory`) and assigning from a mapping, then mutating a field [E2](https://bugbountylibrary.com/r/c4%3A2025-07-gte-spot-clob-and-router%3AH-01.md)[E4](https://bugbountylibrary.com/r/sherlock%3A2024-04-titles%3AM-5.md).
- Functions that zero a field before calling a view helper, where the struct was loaded as `memory` [E7](https://bugbountylibrary.com/r/sherlock%3A2023-12-truflation%3AH-2.md).
- Local declarations whose name matches a contract-level variable in a setter path [E6](https://bugbountylibrary.com/r/c4%3A2025-05-blackhole%3AM-18.md).
- Packed config words passed by value and written back to storage after an earlier call already changed them [E1](https://bugbountylibrary.com/r/cantina%3Ainstadapp_september2024%3A3.1.2.md).
- `migrate()`/layout code calling `read()`/`store()` accessors that still use the old offsets [E3](https://bugbountylibrary.com/r/sherlock%3A2024-08-perennial-v2-update-3%3AM-10.md).
- Periphery caching a parameter the core silently overrides [E5](https://bugbountylibrary.com/r/sherlock%3A2024-02-smilee-finance%3AM-2.md).

## How to review
For every write intended to persist, confirm the target is `storage` (or a direct state variable), not a `memory` parameter, local, or return value. Grep for `memory` on struct parameters and return types of internal setters. Check that no local shadows a state variable on the write path. For packed words, verify the copy written back is the current one, not a stale snapshot. For migrations, diff the documented new layout against the actual bit offsets in `read()`/`store()`. Invariant: after the call, re-reading storage must reflect the intended change.

## Variants seen
- Memory struct parameter in a linked-list insert; `prevOrderId` never persisted, breaking the list [E2](https://bugbountylibrary.com/r/c4%3A2025-07-gte-spot-clob-and-router%3AH-01.md).
- Memory return variable in `_setAcknowledged`; flag discarded [E4](https://bugbountylibrary.com/r/sherlock%3A2024-04-titles%3AM-5.md).
- Memory copy in `cancelVesting`; `locked` stays set, vested funds denied [E7](https://bugbountylibrary.com/r/sherlock%3A2023-12-truflation%3AH-2.md).
- Local `status` shadowing state in governor `execute()`; emission logic reads default [E6](https://bugbountylibrary.com/r/c4%3A2025-05-blackhole%3AM-18.md).
- Stale packed `dexVariables2` written back, resurrecting a cleared bit and causing division by zero [E1](https://bugbountylibrary.com/r/cantina%3Ainstadapp_september2024%3A3.1.2.md).
- Migration accessors unchanged, so `migrate()` is a no-op [E3](https://bugbountylibrary.com/r/sherlock%3A2024-08-perennial-v2-update-3%3AM-10.md).
- Periphery stores a value the core ignores, diverging from actual position [E5](https://bugbountylibrary.com/r/sherlock%3A2024-02-smilee-finance%3AM-2.md).

## Impact and severity
Consequences ranged from locked DEX operations and order-book DoS [E1](https://bugbountylibrary.com/r/cantina%3Ainstadapp_september2024%3A3.1.2.md)[E2](https://bugbountylibrary.com/r/c4%3A2025-07-gte-spot-clob-and-router%3AH-01.md), to users losing vested funds [E7](https://bugbountylibrary.com/r/sherlock%3A2023-12-truflation%3AH-2.md), broken governance/emission behavior [E6](https://bugbountylibrary.com/r/c4%3A2025-05-blackhole%3AM-18.md), failed migrations [E3](https://bugbountylibrary.com/r/sherlock%3A2024-08-perennial-v2-update-3%3AM-10.md), and unburnable positions with stuck funds [E5](https://bugbountylibrary.com/r/sherlock%3A2024-02-smilee-finance%3AM-2.md). Severity is high because the failure is silent and often only surfaces when the affected path is exercised under load or at claim time.

## Evidence
- E1: https://bugbountylibrary.com/r/cantina%3Ainstadapp_september2024%3A3.1.2.md
- E2: https://bugbountylibrary.com/r/c4%3A2025-07-gte-spot-clob-and-router%3AH-01.md
- E3: https://bugbountylibrary.com/r/sherlock%3A2024-08-perennial-v2-update-3%3AM-10.md
- E4: https://bugbountylibrary.com/r/sherlock%3A2024-04-titles%3AM-5.md
- E5: https://bugbountylibrary.com/r/sherlock%3A2024-02-smilee-finance%3AM-2.md
- E6: https://bugbountylibrary.com/r/c4%3A2025-05-blackhole%3AM-18.md
- E7: https://bugbountylibrary.com/r/sherlock%3A2023-12-truflation%3AH-2.md
