diamond-detect 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +38 -21
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -8,7 +8,7 @@ npx diamond-detect .
8
8
 
9
9
  ## Why this tool exists
10
10
 
11
- A Diamond proxy `delegatecall`s into many facet contracts, and **every facet shares the proxy's storage**. When two facets accidentally land at the same slot by reusing a Diamond Storage namespace, by drifting AppStorage layouts, by reusing an EIP-7201 id, or by writing literal slots in inline assembly the result is silent corruption: one facet's writes overwrite another's data with no error and no revert.
11
+ A Diamond proxy `delegatecall`s into many facet contracts, and **every facet shares the proxy's storage**. When two facets accidentally land at the same slot, whether by reusing a Diamond Storage namespace string, by hardcoding the same precomputed slot, by computing the same ERC-7201 namespace inline, by drifting AppStorage layouts, by reusing an EIP-7201 id, or by writing a literal slot directly in inline assembly, the result is silent corruption where one facet's writes overwrite another's data with no error and no revert.
12
12
 
13
13
  Slither catches general storage issues but doesn't speak Diamond. Most teams either hand-audit by spreadsheet or rely on a one-off script. `diamond-detect` is a focused, Diamond-specific analyzer you can drop into CI in three lines of YAML.
14
14
 
@@ -60,23 +60,40 @@ This populates `out/` with artifact JSON files that include the AST and storage
60
60
  diamond-detect .
61
61
  ```
62
62
 
63
- If everything is fine you'll see:
63
+ If everything is fine you'll see a confirmation, plus every storage region the tool verified so you can see it actually inspected each one and that they all sit on distinct slots:
64
64
 
65
65
  ```
66
- scanned 12 contract artifact(s)
67
- ✓ no storage collisions detected
66
+ no storage collisions detected · 8 artifacts scanned
67
+
68
+ Verified 4 storage regions, each on its own slot:
69
+
70
+ • myapp.vaults erc7201 0x84d86c…b71bab LibVaults src/LibVaults.sol:12
71
+ • myapp.strategies namespace 0xa1b2c3…445566 LibStrategies src/LibStrategies.sol:9
72
+ • AAVE_STORAGE_SLOT precomputed 0x340080…215700 AaveFacet src/facets/AaveFacet.sol:28
73
+ • diamond.standard.diamond.storage namespace 0xc8fcad…2c131c LibDiamond src/libraries/LibDiamond.sol:8
74
+
75
+ Every facet keeps to its own namespace, and no two regions share a slot. Nicely done.
68
76
  ```
69
77
 
70
- If something is wrong you'll see one or more findings with the slot, the colliding contracts, and a hint at the cause:
78
+ If something is wrong you'll get one diagnostic per collision, with a code frame pointing at the exact line in every colliding file, the shared slot, and a hint at the cause:
71
79
 
72
80
  ```
73
- ERROR diamond-storage-namespace 0x84d86c34a05b71953e57fe7dafea685384b33934d9ddaebd0cf7709e74b71bab
74
- Diamond Storage namespace "myapp.strategies" is declared in 2 different sources, all resolving to the same slot.
75
- facets: LibStrategies, LibVaults
76
- at src/LibStrategies.sol
77
- at src/LibVaults.sol
81
+ error[diamond-storage-namespace]: Diamond Storage namespace "myapp.strategies" is declared in 2 different sources, all resolving to the same slot.
82
+ ╭─[src/LibStrategies.sol:5:5]
83
+
84
+ 5 │ bytes32 internal constant POSITION = keccak256("myapp.strategies");
85
+ · ────────────────────────────────────────────────────────────────── slot 0x84d86c…b71bab
86
+ ╰─
87
+ ╭─[src/LibVaults.sol:8:5]
88
+
89
+ 8 │ bytes32 internal constant POSITION = keccak256("myapp.strategies");
90
+ · ────────────────────────────────────────────────────────────────── same slot here
91
+ ╰─
92
+ = facets: LibStrategies, LibVaults
93
+ = slot: 0x84d86c34a05b71953e57fe7dafea685384b33934d9ddaebd0cf7709e74b71bab
94
+ = help: give every facet a unique storage seed; never reuse a namespace string, precomputed slot, or formula across facets
78
95
 
79
- 1 error(s), 0 warning(s)
96
+ 1 error · 2 artifacts scanned
80
97
  ```
81
98
 
82
99
  Exit code is `1` whenever a finding meets your `--severity` threshold (default `warn`), `0` otherwise, `2` on internal errors.
@@ -87,7 +104,7 @@ Run [`examples/`](./examples/) to see each one in action — every example ships
87
104
 
88
105
  | Kind | Severity | What it catches |
89
106
  |---|---|---|
90
- | `diamond-storage-namespace` | error | Two libraries declare `bytes32 constant POSITION = keccak256("...")` with the same string. ([01-namespace-collision](./examples/01-namespace-collision/)) |
107
+ | `diamond-storage-namespace` | error | Two facets resolve to the same Diamond Storage slot, whether the slot comes from `keccak256("...")`, a hardcoded precomputed literal (`bytes32 constant S = 0x..`), the inline ERC-7201 formula written without an annotation, or a direct `assembly { x.slot := <literal> }`. All four representations are compared in one space, so a literal in one facet that matches a formula or namespace in another is caught too. ([01-namespace-collision](./examples/01-namespace-collision/)) |
91
108
  | `appstorage-fingerprint` | error | The same fully-qualified struct (e.g. `struct LibAppStorage.AppStorage`) has different layouts across facets — the stale-artifact / forgot-to-rebuild bug. ([02-appstorage-shift](./examples/02-appstorage-shift/)) |
92
109
  | `erc7201-namespace` | error | Two contracts annotate `@custom:storage-location erc7201:<id>` with the same id. ([03-erc7201-collision](./examples/03-erc7201-collision/)) |
93
110
  | `inheritance-overlap` | warn | Two facets have state at the same slot whose `(label, type)` differ — e.g. `Ownable._owner` vs `MyOwnable.owner`. |
@@ -145,7 +162,7 @@ diamond-detect <path> Foundry project root or src/ folder
145
162
 
146
163
  ## Output formats
147
164
 
148
- - **Terminal** (default): coloured, one block per finding, summary footer.
165
+ - **Terminal** (default): a code-frame diagnostic per collision that underlines the exact slot declaration in every colliding file, with `= facets / = slot / = help` notes and a coloured summary footer. A clean run instead lists every storage region it verified, with its slot and location, so you can confirm nothing was skipped. Colour is auto-disabled when the output is piped or running in CI.
149
166
  - **JSON** (`--json`): a stable shape suitable for piping into other tools.
150
167
 
151
168
  ```json
@@ -158,8 +175,8 @@ diamond-detect <path> Foundry project root or src/ folder
158
175
  "slot": "0x...",
159
176
  "message": "...",
160
177
  "facets": ["LibStrategies", "LibVaults"],
161
- "locations": [{ "file": "src/LibStrategies.sol" }],
162
- "detail": { "namespaces": ["myapp.strategies"], "declarations": [...] }
178
+ "locations": [{ "file": "src/LibStrategies.sol", "line": 5, "src": "120:54:0" }],
179
+ "detail": { "namespaces": ["myapp.strategies"], "variableNames": ["POSITION"], "declarations": [...] }
163
180
  }
164
181
  ]
165
182
  }
@@ -206,13 +223,13 @@ Tighten with `--severity error` if you only want to fail CI on hard collisions.
206
223
 
207
224
  ## Comparison
208
225
 
209
- | Tool | Diamond Storage namespaces | EIP-7201 ids | AppStorage drift | Hardcoded sstore slots |
210
- |---|---|---|---|---|
211
- | Slither | partial general slot detector, not Diamond-aware | no | no | yes (separate detector) |
212
- | Hand-audit / spreadsheet | yes, manually | yes, manually | hard to spot | yes |
213
- | `diamond-detect` | yes | yes | yes | yes |
226
+ | Tool | Diamond Storage namespaces | Precomputed / inline-formula slots | EIP-7201 ids | AppStorage drift | Hardcoded assembly slots |
227
+ |---|---|---|---|---|---|
228
+ | Slither | partial, a general slot detector that is not Diamond-aware | no | no | no | partial, raw `sstore` only |
229
+ | Hand-audit / spreadsheet | yes, manually | error-prone by hand | yes, manually | hard to spot | yes, manually |
230
+ | `diamond-detect` | yes | yes | yes | yes | yes |
214
231
 
215
- Slither remains excellent for general Solidity static analysis. Use both.
232
+ Slither's storage layout does not model Diamond namespaced storage, which lives at hashed slots reached through assembly, so it cannot see a Diamond storage collision at all. It remains excellent for general Solidity static analysis, so run both.
216
233
 
217
234
  ## Roadmap
218
235
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diamond-detect",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Static analyzer for EIP-2535 Diamond storage-slot collisions across facets",
5
5
  "homepage": "https://github.com/jayeshy14/Diamond-Storage-Detector#readme",
6
6
  "repository": {