From b88727d7bdce090593db0b68da15e7dc0dc695cd Mon Sep 17 00:00:00 2001 From: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com> Date: Fri, 20 Feb 2026 09:29:34 +0100 Subject: [PATCH] test(contracts): enforce __GAP slot + size invariant in storage layout check (#448) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Adds an arithmetic invariant check (`slot + size == 151`) to `check-storage-layout.sh` that catches cases where a new state variable is added without shrinking `__GAP` accordingly - The existing snapshot-diff check alone could pass with a wrong gap size if the snapshot is updated to match — this new check prevents that - Updates the negative test to also accept the new `__GAP invariant violated!` error message ## Test plan - [x] `check-storage-layout.sh` passes on the current correct layout - [x] `check-storage-layout-negative.sh` passes — the bad layout contract (slot 107 + size 45 = 152) correctly triggers the invariant failure --- .../scripts/check-storage-layout-negative.sh | 2 +- contracts/scripts/check-storage-layout.sh | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/contracts/scripts/check-storage-layout-negative.sh b/contracts/scripts/check-storage-layout-negative.sh index 18d842cf..be79ba9b 100755 --- a/contracts/scripts/check-storage-layout-negative.sh +++ b/contracts/scripts/check-storage-layout-negative.sh @@ -20,7 +20,7 @@ if [ "$EXIT_CODE" -eq 0 ]; then exit 1 fi -if ! printf '%s\n' "$OUTPUT" | grep -q "ERROR: Storage layout has changed!"; then +if ! printf '%s\n' "$OUTPUT" | grep -qE "ERROR: (Storage layout has changed!|__GAP invariant violated!)"; then echo "ERROR: Storage layout check failed, but not for the expected reason." echo "" echo "Output:" diff --git a/contracts/scripts/check-storage-layout.sh b/contracts/scripts/check-storage-layout.sh index 8e2aeb09..390f3298 100755 --- a/contracts/scripts/check-storage-layout.sh +++ b/contracts/scripts/check-storage-layout.sh @@ -59,4 +59,26 @@ if ! diff -q /tmp/snap_normalized.json /tmp/curr_normalized.json > /dev/null 2>& exit 1 fi +# Verify gap invariant: __GAP slot + array size must equal a fixed constant. +# This catches cases where a new variable is added but __GAP is not shrunk accordingly. +EXPECTED_GAP_TOTAL=151 +GAP_SLOT=$(jq '.storage[] | select(.label == "__GAP") | .slot | tonumber' /tmp/current_layout.json) +GAP_SIZE=$(jq -r '.storage[] | select(.label == "__GAP") | .type' /tmp/current_layout.json \ + | grep -oE '[0-9]+' | tail -1) + +if [ -n "$GAP_SLOT" ] && [ -n "$GAP_SIZE" ]; then + GAP_TOTAL=$((GAP_SLOT + GAP_SIZE)) + if [ "$GAP_TOTAL" -ne "$EXPECTED_GAP_TOTAL" ]; then + echo "" + echo "==========================================" + echo "ERROR: __GAP invariant violated!" + echo "==========================================" + echo "" + echo " slot($GAP_SLOT) + size($GAP_SIZE) = $GAP_TOTAL, expected $EXPECTED_GAP_TOTAL" + echo "" + echo "If you added a new state variable, shrink __GAP by the same number of slots." + exit 1 + fi +fi + echo "Storage layout OK - no changes detected"