# Reclaim without existence check

Settle/withdraw paths that move assets before proving the record exists, is active, and belongs to the caller

_Area: Smart contract access control and control flow. Seen in 3 disclosed reports; latest 2024-06-25. Updated 2026-10-23._

## How it works
A settle, stop, or withdraw path acts on a record identified by a caller-supplied key (order hash, lot id, short id) without first proving that key maps to a live, active entry in storage. The code validates a few fields, then transfers assets or writes state. Because the existence/active check is missing or happens after the transfer, a key that is absent, stale, or aliased to another slot still moves value. The broken assumption: "if the fields look valid, the record must exist and belong to this caller."

## Where it shows up
- Reclaim/stop/settle functions that transfer NFTs or tokens before any `require(exists)` or active-status check [E1](https://bugbountylibrary.com/r/c4%3A2024-01-renft%3AH-05.md).
- Validation helpers that check only type, timestamp, or owner, not storage presence [E1](https://bugbountylibrary.com/r/c4%3A2024-01-renft%3AH-05.md).
- Keys (hashes, ids) that omit a field (e.g. the wallet) so distinct records collide [E1](https://bugbountylibrary.com/r/c4%3A2024-01-renft%3AH-05.md).
- Named return variables used to index a mapping before assignment, so the pointer silently aliases slot 0 [E2](https://bugbountylibrary.com/r/sherlock%3A2024-03-axis-finance%3AH-1.md).
- Free-lists of reusable ids where a recreate path does not remove the id from the list [E3](https://bugbountylibrary.com/r/c4%3A2024-03-dittoeth%3AM-02.md).

## How to review
For every settle/reclaim/withdraw path, confirm the invariant: the record is read from storage, confirmed to exist and be active, and bound to the caller, before any asset transfer or state write. Check that the key includes every field that distinguishes records. Trace whether an externally triggered transfer callback can create the record mid-validation [E1](https://bugbountylibrary.com/r/c4%3A2024-01-renft%3AH-05.md).

Two variants are distinct sub-bugs, not missing existence checks, so do not over-apply the invariant above:
- E2 is an ordering bug: the storage pointer is bound before the key is assigned. Grep for `storage ... = mapping[returnVar]` and confirm the index is assigned first [E2](https://bugbountylibrary.com/r/sherlock%3A2024-03-axis-finance%3AH-1.md).
- E3 is a free-list bug: confirm every id-recreate path removes the id from any free-list [E3](https://bugbountylibrary.com/r/c4%3A2024-03-dittoeth%3AM-02.md).

## Variants seen
- Reclaim before existence check, with a transfer callback creating the order mid-validation [E1](https://bugbountylibrary.com/r/c4%3A2024-01-renft%3AH-05.md).
- Storage pointer aliased to slot 0 via unassigned named return, overwriting a prefunded lot [E2](https://bugbountylibrary.com/r/sherlock%3A2024-03-axis-finance%3AH-1.md).
- Reused id not removed from free-list, overwriting an active record [E3](https://bugbountylibrary.com/r/c4%3A2024-03-dittoeth%3AM-02.md).

## Impact and severity
Led to theft of actively rented NFTs and frozen escrow [E1](https://bugbountylibrary.com/r/c4%3A2024-01-renft%3AH-05.md), theft of prefunded auction funds [E2](https://bugbountylibrary.com/r/sherlock%3A2024-03-axis-finance%3AH-1.md), and corrupted debt accounting blocking liquidation [E3](https://bugbountylibrary.com/r/c4%3A2024-03-dittoeth%3AM-02.md). Severity is high when the path moves assets or blocks liquidation; worse when the callback is externally triggered [E1](https://bugbountylibrary.com/r/c4%3A2024-01-renft%3AH-05.md) or the aliased slot holds prefunded value [E2](https://bugbountylibrary.com/r/sherlock%3A2024-03-axis-finance%3AH-1.md).

## Evidence
- E1: https://bugbountylibrary.com/r/c4%3A2024-01-renft%3AH-05.md
- E2: https://bugbountylibrary.com/r/sherlock%3A2024-03-axis-finance%3AH-1.md
- E3: https://bugbountylibrary.com/r/c4%3A2024-03-dittoeth%3AM-02.md
