Ethereum core developers pushed back the Glamsterdam fork on the Hoodi testnet after ChainSecurity found deployed code that relies on gas rules the upgrade will change. The Ethereum Foundation announced the Hoodi activation on October 16, 2026, postponed it on October 22, and on November 6 asked contract developers and auditors to check their projects against eight broken invariants. As of November 9, 2026, no replacement date exists, and the ethereum.org roadmap lists mainnet as a Q4 2026 target with no confirmed day.
That leaves a practical question for anyone who owns a deployed contract: does your failure handling still behave after EIP-8037 and EIP-8038 land? Glamsterdam contract testing answers it with two cheap tools, a property test that sweeps gas limits and a scanner that flags the code patterns the Foundation lists as red flags. Both fit in a 90-minute session, and the whole project ran on Foundry 1.9.0 and Python 3.12.
The Glamsterdam delay and the contracts flagged at risk got plenty of news coverage. What most readers lack is a repeatable way to check their own code. Building one takes eleven steps: break a contract on purpose, watch it fail, fix it, and wire the result into CI.
Don't miss new tech stories on Google
Add Shattered once in the Google app and our stories show up in your news suggestions.
Glamsterdam Contract Testing: Where the Fork Stands
Glamsterdam is the combined name for the next Ethereum execution and consensus upgrades. Execution layer upgrades take the name of a Devcon or Devconnect host city, consensus layer upgrades take a star name, and Glamsterdam merges Gloas with Amsterdam. Two of its gas proposals cause the current trouble. EIP-8037, titled State Creation Gas Cost Increase, raises the price of creating persistent state and meters it separately. EIP-8038, titled State-access gas cost update, reprices reads and writes of existing state.
The timeline matters because the specification is still moving. Here are the dates that are confirmed as of November 9, 2026.
The last row deserves attention. CoinDesk reported on November 9 that two proposals compete to keep old contracts working. One would preserve current gas rates for existing contracts. The other would apply the new rates and add separate accounting for legacy compatibility. Neither is adopted. Code that passes your tests under one outcome might still fail under the other, so the goal is correct failure handling under any reasonable gas schedule, not a fix tuned to today’s numbers.
The missing date changes how you plan. A fork with no scheduled day still has a target quarter, and the Foundation said it needs answers from contract developers before the next AllCoreDev decision. Teams that find a problem early help shape the outcome, because the Foundation said it may adjust EIP-8037 and EIP-8038 depending on the response. Teams that wait inherit whatever schedule ships.
A separate deposit-handling issue also surfaced during testing and got its own write-up on the Glamsterdam deposit bug. That issue concerns validator deposit processing rather than application contracts, so it falls outside the scope of these tests.
The Eight Invariants Glamsterdam Breaks
The Foundation’s invariants repository (named glamsterdam-gas-invariants, updated November 9) lists eight assumptions that stop holding. It labels each one as a Price type, where any change in gas prices breaks it, or an Accounting type, which comes from EIP-8037’s new way of tracking gas. Paraphrased, they map to this project as follows.
INV-8 touches EIP-7702 delegations, which attackers already target in wallet drainer campaigns. The repository’s own guidance says most findings so far reference INV-4 and INV-7, so those two deserve the first look. The runnable tests concentrate on INV-1 and INV-6, because they produce the nastiest outcome: a valid operation recorded as permanently failed, with user funds stuck behind a flag that nobody can reset.
The reservoir drives much of the Accounting group. Under EIP-8037, a transaction whose gas limit exceeds 16,777,216 puts the excess into a state-gas reservoir. State creation draws from that reservoir first. The GAS opcode, exposed in Solidity as gasleft(), reports only the execution gas balance (gas_left). A small gasleft() reading therefore no longer proves that the transaction cannot afford a storage write. The Foundation’s INV-5 write-up on billing by gasleft differences covers the related problem, where gas left can rise during a call and reservoir gas never shows up in it.
Why Failed Calls Became the Main Risk
A failed external call tells the caller nothing about why it failed. The target might have hit a logic error, or it might have run out of gas. The difference matters whenever a contract records a permanent failure, closes a retry window, or removes an item from a queue. A hostile caller can exploit the gap by supplying enough gas for the contract to log the failure but too little for the operation to succeed. ChainSecurity calls this insufficient gas griefing, and the standard defenses fall into three families.
The first defense checks before the call. The contract confirms that the transaction carries enough gas to forward a chosen budget, accounting for EIP-150’s rule that a call forwards at most 63/64 of the remaining gas. The second checks after the call. It treats a small remainder as a sign that the target ate everything it received, and it reverts instead of recording a failure. The third relies on reverts. If the failure handler needs more gas than the leftover provides, the handler itself runs out of gas and the transaction rolls back, which protects the retry path without any explicit check.
Glamsterdam pressures the second and third families. A Blockchain Reporter rundown of the eight gas rules puts the core risk plainly: code after a failed call may still write storage, so a permanent done marker can land where it used to revert. ChainSecurity’s October 13 analysis adds three mechanisms. Reservoir funding changes what the leftover gas can buy. Clearing a slot that was created earlier in the transaction can trigger an immediate refill of execution gas. And a reverting intermediary can hand back gas it already spent, which can lift the remainder above a post-call threshold.
The first defense, the pre-call check, survives best, but only if the budget it protects is large enough for the target’s new costs. That dependency explains why fixed gas numbers (INV-4) top the list of real findings. A budget that covered a token transfer last year can fall short when the recipient is a first-time holder and the transfer creates a new balance slot.
Think about what breaks in practice. Queues for withdrawals, deposits, and batched operations share one shape: process an item, record the outcome, move on. If a failure can be recorded as final for an item that was merely underfunded, the item never runs again. Funds or rights tied to it stay locked behind a flag that no function resets. The project below builds that shape, breaks it, and repairs it.
Prerequisites and Versions
Pin the toolchain before you write any code, because gas behavior differs between EVM targets and compiler releases. The ethereum.org developer tooling page lists Foundry as a current toolchain for compiling Solidity, running unit and fuzz tests, and scripting chain interactions, so it is the natural fit here.
- Foundry: forge 1.9.0 (build dated 2026-11-02). Install with foundryup.
- Solidity compiler: pragma ^0.8.28. Forge resolved solc 0.8.38 on the test machine.
- Python: a recent Python release for the scanner. No third-party packages.
- Git: any recent version, because forge init pulls forge-std.
- OS: Linux, macOS, or Windows through WSL. About 1 GB of free disk.
- Time: 90 minutes, most of it reading test output.
You do not need a wallet, an RPC endpoint, or testnet ETH. Everything runs against Foundry’s in-memory EVM.
One limit up front, and it shapes how to read every result below. Forge 1.9.0 with solc 0.8.38 does not execute Glamsterdam rules. When I asked the compiler for an amsterdam EVM target, it refused and said the version is experimental and needs the settings.experimental option. So the sweep runs under the toolchain’s default rules, where the same failure class exists at different gas thresholds. The test logic carries over unchanged to a Glamsterdam-capable client once your toolchain supports it.
Steps 1 to 3: Scaffold the Project and Write the Vulnerable Contracts
Step 1: Install Foundry and Scaffold the Project
Install Foundry with the official installer, then pull the pinned release. These commands work on Linux, macOS, and WSL.
The version check should print forge Version: 1.9.0 or newer. If your shell cannot find forge, open a new terminal or add the foundry bin directory to PATH.
Now scaffold the project without a git repo and delete the Counter example. The layout keeps vulnerable samples in their own folder so the scanner can exclude them in CI.
Replace foundry.toml with the version below. The default profile fuzzes 256 runs for quick feedback, and a ci profile raises that to 2,000 for the pipeline.
Step 2: Write a Worker That Creates State
The failure only shows up when the target does real state creation, because that is the work EIP-8037 reprices. The Worker contract below writes a configurable number of fresh storage slots on every call. It stands in for any callee that mints, registers, or deposits.
With 80 slots, one call burns a very large amount of gas on today’s rules, since each cold write to a zero slot is expensive. That is large on purpose. It gives the sweep a wide, easy-to-read band of gas limits where the call fails but the caller still has gas left, which is precisely the condition INV-6 describes.
Step 3: Write the Naive Batcher That Triggers INV-1
The NaiveBatcher is the pattern auditors keep finding. It calls a target, ignores why the call failed, and writes a permanent done marker. Anyone can choose the gas for the transaction, so anyone can force the failure branch.
Read the execute function slowly. The call result lands in ok. The next line sets done[id] to true whether ok is true or false. After that, the AlreadyDone check blocks every retry for that id. If a valid operation fails once because the caller supplied too little gas, the batcher records it as finished and the real work never runs.
Why does this contract look safe on today’s rules for most gas limits? Approach C from ChainSecurity’s analysis explains it. When the callee runs out of gas, the caller holds only its retained 1/64 share. Writing the done marker to a fresh slot usually costs more than that share can pay, so the handler itself runs out of gas and the whole transaction reverts. The bug hides because the failure handler is too expensive to finish. Glamsterdam moves the expensive part into a separate reservoir, and the same code can then complete at far more gas limits.
Teams that already run reentrancy tests with Slither and Foundry will recognize the shape. Both bug classes live in what a contract does after an external call returns.
Steps 4 to 6: Sweep and Fuzz the Failure Window
Step 4: Build the Gas Sweep Test
A single failing example convinces nobody. A sweep that prints the outcome at every gas limit shows the whole window. The test file below holds the sweep, the fuzz properties for both batchers, and two checks for the fix. Save it as test/GasSweep.t.sol.
Three details make the sweep trustworthy. The helper _run uses a low-level call with an explicit gas value, so the outer transaction gets exactly the limit under test. Each iteration uses a fresh id, so earlier runs cannot contaminate later ones. And the outcome label separates three cases: the transaction reverted (safe, the caller can retry), the work succeeded, or the batcher recorded a permanent failure (the bug).
Step 5: Read the Window on Today’s Rules
Run only the sweep with verbose logs.
On the test machine the output looked like this, trimmed to the interesting rows.
Below a lower gas limit, the retained 1/64 share cannot pay for the done write, so the transaction reverts and the caller retries. Above an upper limit, the callee gets enough gas to finish. Between them sits a band where the callee runs out while the caller still holds enough to write the permanent marker. My reading of the mechanism: the band opens once one 64th of the starting gas covers the handler’s cold writes, and it closes once 63 of 64 parts cover the worker’s full cost.
Now the point of the exercise. Glamsterdam changes both edges of that band. State creation draws on the reservoir, so the handler’s write needs less execution gas, and the Foundation’s INV-6 entry says the leftover after an out-of-gas call can now be enough to finish and record a failure. The band can open at gas limits where today’s rules revert. Contracts that were safe by accident become exploitable by anyone who picks a gas limit.
Step 6: Fuzz the Invariant and Catch a Counterexample
The sweep uses a fixed 100,000-gas step, which can skip narrow windows. Fuzzing covers the gaps. The property is simple: low gas must never turn a valid operation into a permanent failure. The naive fuzz test encodes it like this.
Run it.
Forge finds a counterexample within a handful of runs and prints the failing calldata.
The argument printed is the raw fuzz input before bound() narrows it, so do not paste it back as a gas value. Use the sweep to read real limits. Forge also saves the failing input under the cache directory and replays it on the next run, which is why this test keeps failing until you fix the contract.
To adapt the sweep to your own contract, replace the Worker with a mock that copies the real target’s state writes, point the batcher at it, and set the sweep range from the target’s gas cost. A quick way to find the range: call the target once with plenty of gas, read the gas used from the trace, and sweep from about half that figure to 1.5 times that figure. Keep the step small enough that the window is at least three steps wide.
This failing test is the deliverable of the first half. In a real audit, a red fuzz test like this one is your evidence for the report, and the access-control tests follow the same pattern of turning a suspicion into a reproducible failing run.
Steps 7 and 8: Ship the Fix and Prove It Holds
Step 7: Ship the Fix With SafeBatcher
The Foundation’s guidance for INV-1 and INV-6 has two parts: let failed items be retried, or revert when the call got too little gas. INV-4 and INV-7 add a third: make gas limits adjustable. SafeBatcher applies all three.
Walk through the four changes. First, execute checks gasleft() before the call and reverts with InsufficientGas unless the transaction carries 64/63 of the budget plus 50,000 gas of overhead. EIP-150 forwards at most 63/64 of the remaining gas, so this pre-check proves the callee can receive its full budget. The check sits before the call, which is the form ChainSecurity describes as approach A. Second, a failed call increments a counter and emits an event instead of setting a terminal flag, so any failure stays retryable. Third, the budget lives in storage and the owner can raise it, so a repricing does not force a redeploy. Fourth, the done flag flips only after a call returns success.
The comment above the call tells the scanner a human reviewed that line. Without it, the INV-1 rule would flag the call because a result is captured. You will see that comment’s effect in Step 10.
The setGasBudget function needs a guard, and the guard needs a test. The suite includes one that expects NotOwner for a stranger. If you want a deeper pattern library for permissioned setters, the smart account module exploit tests cover privilege mistakes in modular wallets.
Step 8: Prove the Fix Holds
The fixed contract has to pass three tests: the fuzz property, a retry check, and the budget change. The fuzz test for SafeBatcher asserts two things after a random gas limit. If done is true, the worker’s last slot must hold its expected value, which proves a real success. If done is false, a retry with 5 million gas must finish the job.
Full output from the project as shipped:
The single failure is the demonstration, not a defect. The naive contract is supposed to lose. To keep a pipeline green, exclude it by name, which Step 11 shows.
Why check the worker’s last slot instead of a flag? A flag could be wrong in the same way the naive contract’s flag is wrong. The last slot of the 80 written by the worker holds its index only when the loop finished, so it is evidence that the work happened and does not depend on the batcher’s own bookkeeping. Independent evidence is what separates a real property test from a test that repeats the implementation.
The budget test deserves a note. It deploys SafeBatcher with a deliberately small gas budget, far too small for 80 slot writes. The first execute call does not revert and does not mark the item done. It records the failure, and after the owner raises the budget, the second call finishes. That sequence is the retry path working under a deliberately wrong parameter.
Steps 9 and 10: Build and Run the glamscan Scanner
Step 9: Build the glamscan Scanner
Property tests need a contract and a worker you can model. A scanner covers the rest of a codebase in seconds. The script below applies one regular expression per invariant, prints file and line, and exits nonzero when it finds high-severity items. It uses only the Python standard library.
Four design choices keep the noise manageable. The INV-1 rule fires only when a call result is captured with the (bool ok, …) form and suppresses itself when a require or revert on failure follows within four lines. That removes the large class of calls that already revert on failure. The INV-6 rule matches the specific shape of a post-call check, a negated result joined to gasleft(), so a pre-call budget check like the one in SafeBatcher passes cleanly. The ignore comment must name the invariant, so a reviewer cannot silence everything by accident. And the exit code is controlled by a threshold flag, which lets a team start with fail-on never, read the findings, and tighten later.
Regular expressions are a blunt instrument. A scanner like this finds candidates, not proofs. For deeper static analysis, pair it with the approach in the Slither smart contract audit walkthrough, and treat every hit as a prompt for the property test from Steps 4 to 6.
Step 10: Run the Scanner and Triage Findings
Scan the full source tree first, so the vulnerable samples show up.
Output on the project:
The Legacy file is a second deliberately flawed sample. Its relay function forwards a hard-coded 50,000 gas (INV-4), bills users by subtracting two gasleft() readings (INV-5), and pays out through transfer, whose 2,300-gas stipend cannot create a storage slot (INV-7). The exit code is 1, which fails a pipeline.
Now scan only the production contracts.
Teams that adopt the scanner usually settle on a simple triage rule. A HIGH finding blocks a merge until someone either fixes it or adds a reasoned ignore comment. A MED finding goes into a ticket with an owner and a date. A finding that appears in a library you do not control gets copied into the threat model instead of the code, since you cannot edit it. That rule keeps the pipeline green without hiding risk, and it leaves a paper trail an auditor can read later.
Zero findings in Worker and SafeBatcher, with one human-approved suppression on the call in SafeBatcher. Treat the suppression as a code review artifact. Keep the reason text after the invariant ID, and review the list of ignores whenever the contract changes. The broader smart contract audit workflow explains where a scan like this sits next to manual review and formal tooling.
Step 11: Wire Glamsterdam Contract Testing Into CI
A one-time check goes stale the day the specification changes. Run both tools on every pull request. The workflow below installs Foundry through the official action, scans the production folder, and runs the tests with the ci profile. It excludes the naive fuzz test by name so the demonstration failure does not block merges.
Assign an owner to the pipeline. Gas rules are a moving target until the fork ships, and a failing gas check three weeks from now should land on a named person, not on whoever happens to read the logs.
I have not run this workflow on GitHub, so treat it as a template. The two commands inside it ran locally exactly as written, apart from the profile variable.
Add one more habit. Re-run the sweep whenever the Foundation or your client team publishes a new devnet build, and compare the window edges to the previous run. A shifted edge is the signal that a spec revision changed your exposure. EIP-8037 already saw revisions between October 24 and November 1, so more movement is plausible.
Common Pitfalls That Break Glamsterdam Tests
Most failed attempts at Glamsterdam contract testing trace back to the same handful of mistakes.
- Testing only the happy path. A suite that runs every call with generous gas never enters the failure branch. Always include a low-gas sweep for any function that catches a failed external call.
- Treating gasleft() as a budget gauge. Under EIP-8037, a small reading does not prove the transaction lacks gas for state creation. Any check that reasons from the leftover value needs a second look.
- Assuming one fix covers both outcomes. Two rival proposals are on the table. Hard-coding a number tuned to one gas schedule trades a bug for a different bug if the Foundation picks the other.
- Forgetting off-chain code. The invariants repository warns that relayers, bots, gas estimators, and receipt parsers can break too. A contract that passes every test can still see failed transactions from a relayer that signs a fixed gas value.
- Silencing the scanner without a reason. An ignore comment with no explanation hides risk from the next reviewer. Write why the call is safe in the same comment.
- Reading the fuzz counterexample as a gas value. Forge prints the raw input before bound() narrows it. Use the sweep output for real thresholds.
- Skipping the retry test. Proving that a failure does not become permanent is half the property. Proving that a later retry succeeds is the other half.
Two habits prevent most of these. First, write the property in plain words before you write any test code: a valid operation must never end as permanently failed because of the gas the caller chose. Second, give every contract under test an adversarial caller, a test account that picks the worst gas limit it can find. Neither habit costs much, and both turn a vague worry about repricing into a failing test you can show a teammate.
Troubleshooting Common Errors
These are the problems most likely to appear when you rebuild the project, with the fix that worked.
- forge: command not found. The installer placed binaries in a foundry bin directory that is not on PATH. Open a new shell, or export PATH with that directory and rerun forge –version.
- forge init fails on a non-empty directory. Run it in an empty folder, or add the force flag. The command in Step 1 assumes a fresh directory.
- Unknown option –no-commit. If your forge release rejects the flag, drop it and run git init yourself if you want a repo.
- Compiler run failed: EVM version amsterdam is experimental. Remove any evm_version = “amsterdam” line from foundry.toml. The compiler refuses that target without the settings.experimental option, and enabling it does not make forge execute Glamsterdam gas rules.
- Source forge-std/Test.sol not found. The lib folder is empty. Run forge install foundry-rs/forge-std, or rerun init in a clean folder.
- The sweep shows no PERMANENT FAILURE rows. Your worker is too cheap or too expensive for the step size. Change the slot count in setUp and rerun. The window needs 1/64 of the starting gas to cover the handler while 63/64 falls short of the worker.
- Fuzz test keeps failing after you fix the contract. Forge replays saved counterexamples. Delete the cache/fuzz folder, or run forge clean, then retest.
- Scanner reports nothing on a file you know is vulnerable. The rules read one line plus a short window. A call split over several lines hides from the single-line regex. Format the call on one line or extend the pattern.
- Scanner flags a safe call. Add the ignore comment above it with the invariant ID and a one-line reason. If many calls need it, tighten the rule instead of suppressing widely.
- Out of gas inside the test itself. The shipped sweep is gas-heavy. If you widen the range and a run reports out of gas, raise gas_limit in foundry.toml or shrink the range.
- CI passes locally but fails on the runner. The runner uses the ci profile with 2,000 fuzz runs, which finds counterexamples the 256-run default missed. Reproduce with FOUNDRY_PROFILE=ci.
Most of these errors share a cause: the toolchain moved while the test stayed put. When something fails in a way the list above does not cover, check three things in order. Confirm the forge and solc versions, clear the cache, and then read the first failing assertion message before looking at any gas number. The assertion messages in the test file were written to point at the broken property, so they usually name the problem faster than a trace does.
Advanced Tips for Real Codebases
Real projects rarely look like the toy above. These extensions carry the method to production code.
Model your actual callee. Replace Worker with a mock that writes the same number of slots your real target writes, including first-time recipient paths. The repository singles out new token balances and new addresses as the cases where costs jump most. A token transfer to an address that never held the token creates a fresh balance slot, and the weird ERC-20 tests show how to build those mock tokens quickly.
Test the reservoir explicitly once your client supports it. The reservoir exists only when the transaction gas limit exceeds 16,777,216. Run each sweep twice, once below that line and once above it, because the failure mode in INV-1 depends on the reservoir being present. Before client support arrives, a spreadsheet of per-path gas costs under the new prices is a reasonable stand-in, and the Foundation’s own agent guidance suggests the same estimation step.
Cover INV-2 and INV-8 by hand. The scanner flags flag resets and open initializers, but the fix needs design thought. For INV-2, move locks into transient storage or keep the resting value non-zero. For INV-8, tie wallet setup to a signature and check the account code after the transaction rather than trusting that a mined transaction applied the delegation.
Add a second fuzzer. Echidna and Foundry invariant tests both express the property differently. ethereum.org updated its Echidna tutorial on November 5 and 6, so the documentation is current. Running two engines on the same property reduces the chance that one generator misses a narrow window.
Match each invariant to a repair. The repository’s suggested fixes, paraphrased, give a short playbook:
- INV-1 and INV-6: let failed items retry, or revert when the call got too little gas, and check gas before the call.
- INV-2: use transient storage for locks and flags, or keep their resting value non-zero.
- INV-3: check the result of CREATE and CREATE2, and never guess why a call failed from the gas left or from empty revert data.
- INV-4 and INV-7: make gas limits and reserves adjustable, estimate under the new rules, and add a safety margin.
- INV-5: charge a fee the user agreed to, or clamp the measurement and cap it, instead of billing by gasleft() differences.
- INV-8: tie wallet setup to a signature and verify the account’s code after the transaction.
Use the repository with an AI agent, with care. The Foundation suggests pointing a coding agent at the invariants repository together with your code. The repo’s AGENTS.md tells the agent to map gas assumptions, search for red flags, estimate gas per path, and report each result as checked or inferred. Treat inferred findings as leads for a human, and keep your source on your own machine.
For more on the wider category, the cryptocurrency coverage hub collects the rest of the smart contract testing series.
The Complete Working Project
Everything below ran as described, and the result is a Glamsterdam contract testing kit you can drop into an existing repository. The tree has seven files you wrote plus forge-std under lib.
The Legacy.sol sample is short enough to type from the findings above: a relayer with a CALL_GAS constant of 50,000, a relay function that bills with a gasleft() difference, and a payOut function that uses transfer. Its only job is to give the scanner something to catch.
Run order for a clean checkout:
The first command restores the dependency, the second gates the production code, the third runs the proof tests, and the fourth prints the failure window for the naive contract. When the Foundation or a client team ships a Glamsterdam-capable test build, rerun the fourth command and record how the window moves.
To extend the project, add one batcher per invariant you care about and keep each failing sample next to its fixed twin. A pair of contracts, one that breaks and one that holds, doubles as documentation for the next engineer who asks why a gas budget is stored in a variable.
Frequently Asked Questions
Is Glamsterdam live on Ethereum mainnet?
No. As of November 9, 2026, it runs on testnets and devnets. It went live on Sepolia on October 6, the Hoodi activation was postponed, and ethereum.org lists mainnet as a Q4 2026 target with no confirmed date.
Which contracts need testing first?
Start with anything that catches a failed external call and then writes state, anything that hard-codes gas amounts, and anything that bills with gasleft() differences. The invariants repository says INV-4 and INV-7 produced most findings so far.
Does a passing scan mean my contract is safe?
No. The scanner reads patterns, not behavior. A clean scan means no known red flag matched. Pair it with the gas sweep on a model of your real callee, and read the eight invariants once by hand.
Can Foundry run Glamsterdam rules today?
Not in the setup tested here. Forge 1.9.0 with solc 0.8.38 rejected an amsterdam EVM target as experimental, and enabling that option changes compiler output rather than adding the new gas accounting. Check your client and toolchain release notes before relying on any Glamsterdam result.
What did the Foundation ask developers to do?
The November 6 post, written by Marius van der Wijden, asked contract developers and auditors to check whether their projects rely on any of the eight invariants. It said ChainSecurity had found projects that would become vulnerable and that the core developers delayed the hard-fork process and will not fork Hoodi until the issues are resolved.
Will the gas rules change again before launch?
They might. EIP-8037 saw specification updates between October 24 and November 1, and two rival proposals for protecting old contracts were reported on November 9. Build tests around behavior, not around specific gas figures, and rerun them after each spec change.
Who found the problem?
ChainSecurity, a Swiss audit firm, published the analysis on October 13, 2026, and the Foundation credited it in the November 6 post. ChainSecurity’s article recommends testing failure handlers, retry paths, gasleft() checks, and state-creation behavior.
Should I pause deployments until the fork ships?
No official guidance on pausing exists in the sources reviewed. The Foundation’s request is to check your projects against the eight invariants and report affected ones. Running the sweep and scanner before any new deployment is a cheap way to follow that request.
Do off-chain systems need checks too?
Yes. Relayers, bots, gas estimators, and tools that read receipts can break when gas accounting changes. The repository calls this out for INV-4 and INV-8 specifically.
Laura Bennett edits and fact-checks shattered.io security and privacy coverage, making sure each piece is clear, current, and grounded in primary sources.
![Glamsterdam Contract Testing: 11 Steps, 90 Min [2026]](https://shattered.io/wp-content/uploads/2026/11/glamsterdam-contract-testing-foundry-scanner-11-steps-2026-gen-1.webp)

