ByAUJay
Summary: Out-of-gas (OOG) isn’t just a dev headache—it’s a P&L leak. This playbook shows how DeFi teams can systematically reproduce, diagnose, and eliminate OOG in complex, multi-contract transactions, tying Solidity- and ZK-level fixes directly to lower failure rates, better aggregator routing, and measurable fee savings.
Debugging “Out of Gas” Errors in Complex Transactions
Target audience: DeFi protocol teams focused on “Gas optimization,” execution reliability, and aggregator routing. We’ll bridge Solidity/EVM/ZK details with business outcomes: lower revert rates, better fill quality, and higher net volume.
The specific OOG you’re likely facing (and why it’s hard to reproduce)
You ship a sophisticated transaction: multi-hop swap through a Uniswap v4 hook, a permit flow, a callback, maybe a proof verification—and it reverts with “Out of Gas.” On mainnet, the same call sometimes succeeds, sometimes fails, depending on routing, storage “warmth,” and call depth. On L2, it bombs in edge cases after an innocuous config change. Meanwhile, gas reporters say one thing, production does another.
The most common root causes we keep seeing in 2025–2026 across DeFi:
- Cold vs warm state access penalties cause step-function jumps in gas, depending on access order and whether you “pre-warm” storage or accounts. First SLOAD in a tx now costs 2100 gas vs 100 when warm; first external call targets cost 2600 gas, with notable implications for deep call trees and proxy hops. (eips.ethereum.org)
- The 63/64 rule drains forwarded gas across nested CALL/DELEGATECALL trees; a few additional frames (e.g., hooks + callbacks) tip routes into OOG even if top-level gas looked healthy. (github.com)
- Memory expansion is quadratic; one unchecked abi.encode/decoding or copy can silently balloon memory and blow the budget. (evm.codes)
- Contract creation has new “intrinsic” initcode metering and size failure modes (EIP-3860), so factories that were fine pre-Shanghai now fail sporadically when constructor payloads spike. (eips.ethereum.org)
- Refund-era habits no longer help: refunds on SELFDESTRUCT were removed and SSTORE refunds reduced (EIP-3529). Over-optimizing for refunds today often backfires. (eips.ethereum.org)
- Post-Dencun, blob DA lowered L2 fees drastically, but calldata to EVM (e.g., proofs, large params) still costs 4/16 gas per byte, so “data-heavy” L2 interactions can remain gas-driven. (ethereum.org)
- New opcodes changed the optimal patterns: MCOPY (EIP-5656) makes looped memory copies obsolete; transient storage (EIP-1153) transforms reentrancy/context patterns but is easy to misuse and leave stale flags that let attackers bypass checks. (soliditylang.org)
This complexity couples with shifting capacity targets. Ethereum’s block gas limit rose beyond ~30M in February 2025 and later toward mid‑30s/45M depending on validator signaling. Your headroom changed—but so did the rules-of-thumb that used to “just work.” (theblock.co)
Why this is more than a dev nuisance
- Missed routing windows: Aggregators demote routes with high revert rates. OOG at p95 raises “effective price” and reduces inclusion odds during volatile blocks.
- MEV exposure: A transaction that just fits at simulation but OOGs on-chain is a gift to opportunistic searchers—your users see worse slippage or missed fills.
- Launch timing and procurement risk: a delayed liquidity program or TGE because of flaky mainnet execution is materially expensive. Gas regressions discovered late in audit can push schedules into the next fiscal quarter.
- Technical debt compounds: using transfer/send (2300 stipend) or storage-based reentrancy guards is now a liability; EVM re-pricing already invalidated old assumptions and Solidity 0.8.31 officially deprecated transfer/send with removal slated for 0.9.0. (soliditylang.org)
7Block’s “Technical but Pragmatic” methodology
We apply a production-first process that correlates EVM-level traces with business KPIs (revert rate, fill rate, and net fees). Engagements slot into your sprint cadence and procurement workflows, and are backed by our custom toolchain plus open tooling.
- Reproduce deterministically with structured traces
- Stand up deterministic repros using Foundry/Hardhat on forks at target blocks. Trace with geth’s struct logger, callTracer, and custom JS tracers to annotate cold/warm transitions, per-frame gas, and 63/64-forwarded gas. (geth.ethereum.org)
- For CI, we enable “–trace/–fulltrace” in Hardhat Tracer and Foundry debugger sessions to isolate the exact opcode/memory expansion that spikes costs. (npmjs.com)
- Quantify hot spots fast
- Generate function-level gas reports and snapshots in Foundry; diff per-commit to prevent regression drift. (learnblockchain.cn)
- On L2s, interpret gas reports with the chain’s fee model caveats (e.g., zkSync bootloader overhead). (foundry-book.zksync.io)
- Eliminate common OOG triggers with modern EVM patterns
- Cold/warm awareness (EIP-2929): restructure storage access to batch reads/writes after a single warm-up. Consider access lists (EIP-2930) when the touched set is predictable; we model their breakeven against your execution path. (eips.ethereum.org)
- Calldata discipline (EIP-2028): aggressively minimize non-zero bytes in calldata; apply short-ABI patterns where appropriate; this is critical on L2 where calldata dominates costs. (eips.ethereum.org)
- Memory: replace manual copy loops with MCOPY; budget memory expansion using the Yellow-Paper formula to avoid quadratic surprises. (eips.ethereum.org)
- Reentrancy/context: swap storage-based guards for EIP-1153 transient storage patterns; we ship hardened templates that always clear TSTORE and check context across callbacks. We also include “unsafe pattern” tests seeded by recent incidents. (eips.ethereum.org)
- ETH transfers: replace transfer/send with call and explicit reentrancy protections; this aligns with Solidity 0.8.31 deprecation guidance. (soliditylang.org)
- Design for call depth and 63/64
- Collapse deep call stacks where viable (e.g., fold adapter hops), avoid gratuitous DELEGATECALL chains, and ensure hooks/callbacks are structured to leave room at the bottom of the gas waterfall. (github.com)
- Creation paths: comply with EIP-3860 (initcode metering) and cap runtime size
- Audit factories and CREATE2 paths for initcode length and per-word costs; use minimal proxies (EIP‑1167 style) or split constructors to avoid exceptional aborts that appear as OOG. (eips.ethereum.org)
- ZK verification that fits in-budget, every time
- If you verify proofs on-chain, enforce three‑pairing Groth16 verifiers on BN254 or evaluate BLS12‑381 post-Pectra; we’ll count pairings and public inputs so your verify is ~200–300k gas instead of surprise 500k+. (eips.ethereum.org)
- For higher throughput, we design batching/aggregation or leverage verification layers to flatten per-proof gas while controlling latency. (7blocklabs.com)
- L2 and Dencun-aware routing
- Where feasible, push data to blobs (Type‑3 blob transactions) instead of calldata to realize the 10–100× fee reductions rollups saw after EIP‑4844. We’ll update routers/relayers accordingly and keep proof calldata lean, since verifiers still read calldata, not blobs. (ethereum.org)
- Rollout: guardrails and monitoring
- Ship gas budgets per pathway, p95/p99 gas SLOs, and on-chain telemetry for revert codes vs OOG buckets. CI enforces “gas budget not to exceed” per critical function.
Practical examples (with precise, current tactics)
A) Nested multi-hop swap + hook + callback OOG due to 63/64 and cold access
- Symptom: succeeds in local simulation, fails on mainnet when a route chooses a different pool ordering.
- Fixes we apply:
- Warm the exact storage slots you must touch early in the call path (or use EIP‑2930 access lists if the touched set is deterministic). Then batch reads/writes post-warm. (eips.ethereum.org)
- Flatten unnecessary internal calls in hooks; move heavy logic off critical path and use transient storage to pass context across callbacks at ~100 gas per tload/tstore. (eips.ethereum.org)
- Ensure sufficient top-level gas to survive 63/64 cascading reserves across a few frames; we give you a per-frame gas budget with safety margins. (github.com)
B) Constructor-heavy deployment path fails with OOG at CREATE/CREATE2
- Symptom: unpredictable OOG at deployment on Shanghai+ networks.
- Cause: EIP‑3860 adds initcode metering (2 gas per 32-byte chunk) and a 49,152-byte initcode size cap; “too large” or too many dynamic copies can fail before constructor runs. (eips.ethereum.org)
- Fixes: split initialization across transactions, adopt EIP‑1167 minimal proxies, and remove unnecessary constructor-time abi.encode of large arrays.
C) Memory blowup from abi.encodePacked for large arrays
- Symptom: gas spikes only on certain batch sizes.
- Fix: for large copies use MCOPY (Solidity 0.8.25 enables codegen that replaces MLOAD/MSTORE loops), and precompute required memory size to avoid quadratic expansion surprises. (soliditylang.org)
D) Reentrancy guard and callback context using storage is too costly; sometimes OOG on cold paths
- Symptom: infrequent OOG on first interaction per tx; high average gas.
- Fix: migrate guard/context to EIP‑1153 transient storage. We provide a hardened pattern that always clears state on both success and revert paths, avoiding “stale TSTORE” vulnerabilities observed in 2025. Savings are ~6,900 gas per guarded function vs storage-based patterns. (eips.ethereum.org)
E) On-chain proof verify intermittently OOGs when public inputs increase
- Symptom: verification passes on small inputs but fails on larger routes.
- Fix: switch to a 3‑pairing Groth16 template on BN254 (pairing gas ≈ 34k·k + 45k) and budget ~207,700 + 7,160 × l gas (l = public inputs). We implement parameterized verifiers and guardrails that precheck gas against l. (eips.ethereum.org)
F) Uniswap v4 hooks deployment and swap paths
- Guidance: lean on the singleton PoolManager and flash accounting to reduce inter-contract transfers; avoid expensive per-swap storage writes in hooks; model hook gas so routes stay competitive. Official docs: lower pool creation and more efficient multi-hop via singleton/flash accounting. (docs.uniswap.org)
Emerging best practices we implement by default
- Prefer call over transfer/send and guard explicitly; Solidity has deprecated transfer/send in 0.8.31. (soliditylang.org)
- Use short-ABI and calldata-minimization patterns, especially for L2 where calldata dominates cost. (ethereum.org)
- Treat access lists as a surgical tool, not a blanket policy; we compute the breakeven vs penalties using your actual access set. (eips.ethereum.org)
- Adopt MCOPY (EIP‑5656) for any sizable memory copies; let the compiler target Cancun EVM (Solidity 0.8.25+) for automatic wins. (soliditylang.org)
- Use transient storage (EIP‑1153) for reentrancy guards and per-tx context, but always clear; never carry security-critical state across tx boundaries. (eips.ethereum.org)
- If you’re verifying ZK proofs: pin to a curve/precompile you can budget, prefer 3-pairing checks, and consider aggregation or verification layers when proofs-per-epoch is large. (eips.ethereum.org)
- On L2 post‑Dencun, move DA to blobs; keep EVM calldata tight; measure fee impacts (rollups saw 90%+ fee reductions after 4844). (ethereum.org)
Implementation snippets (Solidity ≥0.8.25 / Cancun EVM)
- Transient reentrancy/context (cheap and safe)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.25; abstract contract TransientReentrancyGuard { // keccak256("reentrancy.lock") bytes32 private constant LOCK_SLOT = 0x8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4; error Reentrancy(); modifier nonReentrant() { assembly { if tload(LOCK_SLOT) { mstore(0x00, 0x37ed32e8) revert(0x1c, 0x04) } tstore(LOCK_SLOT, 1) } _; assembly { tstore(LOCK_SLOT, 0) } } }
- TLOAD/TSTORE cost ≈ 100 gas each; do not forget to clear on every exit path, including reverts in inner calls. (eips.ethereum.org)
- Memory copies with MCOPY (no loop)
pragma solidity ^0.8.25; library BytesLib { function concat(bytes memory a, bytes memory b) internal pure returns (bytes memory r) { r = new bytes(a.length + b.length); assembly { // dst, src, len mcopy(add(r, 0x20), add(a, 0x20), mload(a)) mcopy(add(r, add(0x20, mload(a))), add(b, 0x20), mload(b)) } } }
- MCOPY gas ≈ 3 + 3·words + memory expansion; the compiler now emits it for many copies automatically. (eips.ethereum.org)
- Access lists when storage access set is predictable (tx type 1)
- For batched admin ops across known slots, we’ll prebuild an access list so reads are warm (100 gas) instead of cold (2100). The cost model is validated before enabling, since over-inclusion wastes gas. (eips.ethereum.org)
- Proof verify budgeting (Groth16)
- Target 3 pairings and count public inputs; BN254 pairing gas ≈ 45,000 + 34,000·k; total ≈ 207,700 + 7,160·l. Guard with require(gasleft() > threshold(l)). (eips.ethereum.org)
Proof: GTM metrics we instrument and commit to
We tie engineering changes to commercially relevant metrics so Procurement has a basis for ROI.
-
Reliability KPIs (after fixes):
- Revert rate on critical routes: from baseline X% to target <0.5% (p95 gas within budget and no OOG in hook paths).
- Aggregator scorecards: restore routing share by lowering “effective gas per fill” and failure penalties (tracked per-aggregator integration).
-
Performance KPIs:
- Per-swap gas reduction: typical 15–35% from MCOPY adoption, transient guards, cold/warm clustering, and eliminating superfluous logs/calls. Compiler (0.8.25+) alone yields measurable wins on byte array ops. (soliditylang.org)
- Proof verification gas: bring Groth16 verify into 220k–280k range at target l, or aggregate to flatten per-proof cost. (medium.com)
- L2 fee reduction alignment: where data can move to blobs, realize 90%+ fee reductions observed post‑4844 on major rollups. (thedefiant.io)
-
Financial model (illustrative):
- If we remove 60,000 gas/tx, at 5 gwei and $3,000/ETH that’s ≈$0.90 saved per tx. At 1,000,000 monthly tx, that’s ≈$900,000/month—without counting higher inclusion rates that raise volume. Your actuals vary with gas price and ETH, but the unit economics scale linearly.
-
Delivery metrics:
- Triage to first reproducible trace: 3–5 business days.
- Pilot hardening of top three flows: 4–6 weeks including audits and canary rollout.
- p95 gas SLOs and revert budgets enforced in CI/CD thereafter.
Why 7Block Labs
- We pair core-EVM changes (EIPs 2929/2930/3529/3860/5656/1153) with pragmatic engineering, not academic advice. This includes geth-level tracing, Solidity compiler behaviors post‑Cancun, and Uniswap v4 hook ergonomics. (eips.ethereum.org)
- ZK pragmatism: we implement verifiers you can actually budget (BN254/BLS12‑381 precompiles, correct pairing counts), and design aggregation paths when proof volume grows. (eips.ethereum.org)
- Post‑Dencun execution economics: we migrate appropriate data to blobs while keeping EVM calldata lean where it still matters (verifiers, router params). (ethereum.org)
How we engage (and where we plug in)
- Architecture and protocol engineering: from Uniswap v4 hook design to proof verification modules via our custom smart contract development and DeFi development services.
- End-to-end product builds: if you need a new module or adapter, our web3 development services and custom blockchain development services teams deliver it.
- Security and gas audits: a combined pass that flags OOG risks alongside standard vulns via our security audit services.
- Cross-chain design and integrations: bridges, routers, and L2 adjustments via our blockchain integration, cross-chain solutions development, and blockchain bridge development.
Quick checklist you can run this week
- Upgrade compiler to ≥0.8.25 (Cancun EVM): pick up MCOPY codegen and transient-storage warnings. (soliditylang.org)
- Replace any transfer/send with call + guardrails per Solidity deprecation. (soliditylang.org)
- Instrument traces on your top three routes with geth callTracer and Hardhat Tracer; snapshot p95/p99 gas. (geth.ethereum.org)
- Convert storage-based reentrancy guards to transient storage; include thorough tests for “stale TSTORE” on all revert paths. (eips.ethereum.org)
- For any CREATE/CREATE2 factory that’s near limits, measure initcode size/cost against EIP‑3860; refactor to minimal proxies where needed. (eips.ethereum.org)
- If you verify proofs, count pairings and public inputs; aim for ~220k–280k verify gas or aggregate. (medium.com)
- On L2, move DA to blobs where possible and shrink calldata with short-ABI patterns. (ethereum.org)
If you’re feeling the pain of OOG in complex transactions—and the business impact it creates—our team can help you ship fixes that stick, and quantify the ROI in your GTM dashboard.
Book your DeFi Gas Optimization Audit (90 minutes) today.
Like what you're reading? Let's build together.
Get a free 30‑minute consultation with our engineering team.

