Skip to main content
Zeroing out state means setting stored contract values back to their defaults (e.g., 0, false, address(0)) by writing non-zero → zero into EVM storage slots.

Why Zero Out State

Non-zero storage contributes to global state size. Clearing stale state reduces state growth pressure and can improve node operations, sync, and restart behavior. This matters especially on high-throughput chains like Sei where state growth can be aggressive.
Clearing a non-zero storage slot to zero earns a 4,800 gas refund per slot.

Clearing State Variables, Arrays, and Maps

Simple Fixed-Size State

For value types and fixed-size arrays, use delete (or assign the default) to reset values to their defaults (0, address(0), false).

Dynamic Arrays

Using delete on a dynamic array resets its length to 0 and clears all elements, but this can be too expensive for large arrays and risks hitting the block gas limit. The safer approach is batched clearing by repeatedly calling pop() in chunks.
If a dynamic array might exceed ~500 elements, use batched clearing to avoid running out of gas.

Mappings

You can’t iterate a mapping, so you can’t clear it unless you can enumerate keys. The main strategy is to maintain an index of keys (e.g., a holders[] array plus an _isHolder flag) whenever you write, then later delete mapping entries in batches by iterating that key list.
Maintaining an index costs roughly one extra SSTORE per write (Sei: ~72,000 gas; see EVM Differences), but makes future clearing possible.

Strategies for Existing Contracts

External Cleaner Contract

If you control permissions and the original contract has callable setters/entrypoints, deploy a separate “cleaner” contract that loops through user/key batches and calls the original contract to set values to zero.
This only clears state reachable through the public/external interface. Truly internal or private state with no setters can’t be touched.
See Appendix A for example code.

Proxy / Upgradeable Contract

If you have a proxy or upgradeable contract, this is the best-case scenario. Upgrade the implementation to add reset functions while preserving storage layout, run batched resets, then optionally upgrade again to remove the reset logic. See Appendix B for example code.

Reconstruct Keys from Event Logs

If contract writes emit events (which is common), scan historical logs to recover mapping keys (e.g., recipients from Transfer events), deduplicate them, and pass that list into a batch-clear call.

Use a Subgraph or Custom Indexer

For complex or nested state, use The Graph or your own indexer to build a full set of non-zero keys/entities off-chain, then feed the resulting address/key list into on-chain batch clearing. This moves enumeration complexity off-chain while the on-chain work is just applying clears.

Brute-Force via Direct Storage Reads

If you have a candidate universe of keys (e.g., all addresses that ever interacted), compute each mapping slot (keccak256(key, slot) — tools like cast index do this), read storage via RPC, and keep only the non-zero keys to clear.
This approach is tedious and RPC-heavy but works when events are missing or unreliable.

Appendix A - External Cleaner Contract

Appendix B - Proxy/Upgradeable Contract