Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/web/e2e/storage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,15 @@ test('a reserved slot shows the value the constructor put in it', async ({ page
const reserved = grid.locator('[data-testid="storage-reference"]');
await expect(reserved.nth(0)).toContainText('length = 1', { timeout: 60_000 });
await expect(reserved.nth(1)).toContainText('"hi"');

// A single push touches two slots (the length it rewrites, plus the new
// element) but names only one derived entry — the length slot is the
// reserved slot itself, not a derived one, and must not be double-counted.
const historyRecord = grid
.locator('[data-testid="storage-record"]')
.filter({ hasText: 'history' });
await expect(historyRecord).toContainText('1 entry seen');
await expect(historyRecord).not.toContainText('2 entries seen');
});

test('the default source shows its reserved slots, then their values on deploy', async ({
Expand Down
7 changes: 5 additions & 2 deletions apps/web/src/storage/StorageGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,11 @@ export function StorageGrid({
// prose that used to live under "Not expanded".
const head = row.segments[0];
const referenceSpan = head?.kind === 'reference' ? head.span : null;
const derivedSeen =
referenceSpan === null ? 0 : (runtime?.derivedCountByBase.get(referenceSpan.path) ?? 0);
// The same count that drives the child rows below, so the chip and
// the rows can never disagree. Counting resolutions by baseVariable
// instead double-counted a dynamic array's own length slot (read on
// entry, written on exit) as if it were a second entry.
const derivedSeen = children.length;
const recordCells = row.segments
.filter((segment) => segment.kind === 'value')
.map((segment) => (segment as Extract<RowSegment, { kind: 'value' }>).cell)
Expand Down
35 changes: 1 addition & 34 deletions apps/web/src/storage/runtimeView.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { SemanticResolution, StorageAccessObservation } from '@slotscope/domain';
import type { StorageAccessObservation } from '@slotscope/domain';
import { describe, expect, it } from 'vitest';
import { buildRuntimeView } from './runtimeView';

Expand All @@ -17,28 +17,10 @@ function trace(storage: StorageAccessObservation[]) {
return { observations: { keccak: [], storage } };
}

function resolution(partial: Partial<SemanticResolution>): SemanticResolution {
return {
accessId: 'step-0',
stepIndex: 0,
kind: 'sstore',
status: 'exact',
label: null,
decodedValues: [],
reasoning: [],
baseVariable: null,
accessCostNote: null,
migration: null,
previousValueHex: null,
...partial,
};
}

describe('buildRuntimeView', () => {
it('is empty without a trace', () => {
const view = buildRuntimeView(null, [], 0);
expect(view.wordsBySlot.size).toBe(0);
expect(view.derivedCountByBase.size).toBe(0);
});

it('keys words by decimal slot and lets the last observation win', () => {
Expand Down Expand Up @@ -79,21 +61,6 @@ describe('buildRuntimeView', () => {
);
expect(view.wordsBySlot.get('1')).toBe(`${'0'.repeat(63)}4`);
});

it('counts distinct derived entries per base variable', () => {
const view = buildRuntimeView(
trace([]),
[
resolution({ baseVariable: 'balances', label: 'balances[0x01]' }),
resolution({ baseVariable: 'balances', label: 'balances[0x01]' }),
resolution({ baseVariable: 'balances', label: 'balances[0x02]' }),
resolution({ baseVariable: null, label: 'raw' }),
],
9,
);
expect(view.derivedCountByBase.get('balances')).toBe(2);
expect(view.derivedCountByBase.has('raw')).toBe(false);
});
});

describe('address-space filter (v1.5)', () => {
Expand Down
16 changes: 1 addition & 15 deletions apps/web/src/storage/runtimeView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import type { SemanticResolution, TraceObservations } from '@slotscope/domain';
export interface StorageRuntimeView {
/** Decimal slot → the last 32-byte word observed at or before the cursor. */
readonly wordsBySlot: ReadonlyMap<string, string>;
/** Base variable → how many distinct derived entries this run has named. */
readonly derivedCountByBase: ReadonlyMap<string, number>;
/** Raw Layer-1 facts, so the grid can reconstruct derived slots itself. It
* needs the declared-slot set to do that, which only the grid knows. */
readonly observations: TraceObservations | null;
Expand All @@ -13,7 +11,6 @@ export interface StorageRuntimeView {

const EMPTY: StorageRuntimeView = {
wordsBySlot: new Map(),
derivedCountByBase: new Map(),
observations: null,
resolutions: [],
};
Expand Down Expand Up @@ -56,16 +53,5 @@ export function buildRuntimeView(
}
}

const derivedLabels = new Map<string, Set<string>>();
for (const resolution of resolutions) {
const base = resolution.baseVariable;
if (base === null) continue;
const labels = derivedLabels.get(base) ?? new Set<string>();
labels.add(resolution.label ?? resolution.accessId);
derivedLabels.set(base, labels);
}
const derivedCountByBase = new Map<string, number>();
for (const [base, labels] of derivedLabels) derivedCountByBase.set(base, labels.size);

return { wordsBySlot, derivedCountByBase, observations, resolutions };
return { wordsBySlot, observations, resolutions };
}