Transient storage as its own address space - #8
Open
halaprix wants to merge 4 commits into
Open
Conversation
slotscope-zvd gave every reserved slot a row but could not show what lives in a mapping, because those entries sit near 2^256, nowhere near the declared sequence. They now appear as child rows under the head they descend from. The address is written the way the EVM computes it — keccak(0xaa…, 1), or keccak(0xbb…, keccak(0xaa…, 2)) for a nested hop, or keccak(3)+2 for an array element — and that string goes in the slot rail, which is the address column everywhere else in this instrument. The raw 32-byte slot is a hover detail. The derivation is the identity, because it is the thing worth learning. Every child is reconstructed from Layer-1 facts alone: KeccakObservation carries the exact preimage, so a 64-byte input splits into key and base, a 32-byte input is an array or data head, and a nested hop is followed until its tail is a declared slot. Parent linkage is therefore slot arithmetic, not name matching — a head nested inside a struct attaches with no special case, and the derivedCountByBase name-matching gap from v1.2 disappears rather than being patched. buildRows stays compiler-only: the gapless invariant is about DECLARED slots, and a derived slot is not one. Children attach at the view layer. Decisions worth keeping: - The child set spans the whole run rather than stopping at the cursor. Roving focus is index-based, so a row appearing mid-scrub would move focus under the reader; the cursor changes values, never row positions. - Read-only entries are included and marked. Nothing surfaced them before, and "read but never written" is worth teaching. - A touched slot whose derivation cannot be reconstructed becomes a visible orphan, never a silent drop. - RuntimeStorage becomes a write log in observation order. The grid is the map; two maps of the same state only made a reader pick one to believe. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Transient storage (EIP-1153) gets its own tab and its own grid. It is never merged with persistent storage, because both spaces number from 0 — in the probe that shaped this design, the persistent `persistent` is slot 0 and the transient `lock` is slot 0 — so a merged view would make slot 0 mean two things at once. Keeping them apart is the lesson, not an implementation detail. Probed against the locked solc 0.8.36 before designing anything: - transientStorageLayout exists with the same shape as storageLayout, so decodeStorageLayout and buildRows work on it verbatim; - packing is identical (two uint128 transient vars share slot 2 at 0 and 16); - transient is VALUE TYPES ONLY — mapping, struct and static array are rejected with UnimplementedFeatureError. That last point is why this phase is small: the transient grid needs none of the v1.2/v1.4 reference machinery. No reserved heads, no derived children, no elided ranges. StorageGrid is reused unchanged behind a `space` prop that only selects copy and switches derived-child reconstruction off, since reconstructing against persistent keccaks could only invent a link. Transient accesses are now named: resolveAccesses takes an optional transient layout and routes strictly by kind, so neither index is ever consulted for the other space's accesses. docs/limitations-v1.0.md is updated accordingly — that entry was true only while the persistent layout was the sole index. Honesty about lifetime is carried in permanent text, not styling. Before a run: "transient storage is empty outside a transaction". After one: "observed during the transaction, discarded when it ended". The tab itself appears only when the contract declares transient variables, so its absence is compiler truth rather than a UI accident. The locked Standard JSON snapshot moved deliberately to request the new output. Fixture goldens verify unchanged, which confirms the added selection changes what solc reports, not what it compiles. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements
docs/design/transient-storage-v1.5.md. Closesslotscope-syp(.1–.3).Why it is a separate space, not a second column
Transient storage (EIP-1153) numbers from 0 in its own address space. In the
probe that shaped this design, the persistent
persistentis slot 0 and thetransient
lockis slot 0. A merged view would make slot 0 mean two things atonce, so transient gets its own tab and its own grid. Keeping them apart is the
lesson, not an implementation detail — there is an e2e that asserts neither grid
ever shows the other's name.
What solc actually gives us
Probed against the locked solc 0.8.36 before designing anything, because the
whole shape depended on it:
transientStorageLayoutalready exists, same shape asstorageLayout. SodecodeStorageLayoutandbuildRowswork on it verbatim — no new decoder. Itsimply was not in our
outputSelection.uint128 transientvariables shared slot 2 at offsets0 and 16.
mapping, structs and static arrays are rejected withUnimplementedFeatureError: Transient data location is only supported for value types.Point 3 is why this phase is small: the transient grid needs none of the v1.2/v1.4
reference machinery — no reserved heads, no keccak-derived children, no elided
ranges.
StorageGridis reused unchanged behind aspaceprop that selects copyand switches derived-child reconstruction off, since reconstructing a transient
slot against persistent keccaks could only invent a link.
Transient accesses are finally named
docs/limitations-v1.0.mdrecorded that transient accesses never resolve. That wastrue only while the persistent layout was the sole index — naming them against it
would have named the wrong variable.
resolveAccessesnow takes an optionaltransient layout and routes strictly by kind, so neither index is ever consulted
for the other space's accesses. Without this the tab could label nothing.
Honesty about lifetime
Transient state is discarded when the transaction ends, so the tab never claims a
"current" value. Two permanent text lines carry it, not styling:
transient storage is empty outside a transaction;observed during the transaction · discarded when it ended.The tab itself appears only when the contract declares transient variables, so its
absence is compiler truth rather than a UI accident.
Note on the locked input
The Standard JSON snapshot moved deliberately to request the new output. Fixture
goldens verify unchanged, which confirms the added selection changes what solc
reports, not what it compiles — the property that matters for a locked
environment.
Verification
format:check,lint,typechecktest:unittest:e2efixtures:verifyrelease:verifybuild:checkbenchmark:v0.1🤖 Generated with Claude Code