Skip to main content
Zeroing out state means setting stored contract values back to their defaults, such as 0, false, or address(0). To do this, you write non-zero → zero into EVM storage slots.

Why zero out state

Non-zero storage adds to the global state size. When you clear stale state, you reduce pressure from state growth and can improve node operations, sync, and restart behavior. This matters especially on high-throughput chains such as 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 value. This resets the values to their defaults (0, address(0), false).

Dynamic arrays

If you use delete on a dynamic array, it resets the length to 0 and clears all elements. For large arrays, this can be too expensive and can hit the block gas limit. The safer approach is batched clearing: call pop() repeatedly, in chunks.
If a dynamic array might exceed approximately 500 elements, use batched clearing so that you do not run out of gas.

Mappings

You cannot iterate over a mapping, so you cannot clear it unless you can enumerate its keys. The main strategy is to maintain an index of keys whenever you write. For example, use a holders[] array plus an _isHolder flag. Later, iterate over that key list to delete mapping entries in batches.
Maintaining an index costs roughly one extra SSTORE per write, but it makes future clearing possible. On Sei, that extra write costs approximately 72,000 gas. See EVM differences.

Strategies for existing contracts

External cleaner contract

If you control permissions and the original contract has callable setters or entry points, deploy a separate “cleaner” contract. The cleaner loops through batches of users or keys and calls the original contract to set values to zero.
This method clears only state that is reachable through the public or external interface. It cannot change internal or private state that has no setters.
See Appendix A for example code.

Proxy / upgradeable contract

If you have a proxy or upgradeable contract, this is the best case. Upgrade the implementation to add reset functions, and preserve the storage layout when you do. Run the batched resets. Then you can upgrade again to remove the reset logic. See Appendix B for example code.

Reconstruct keys from event logs

Contract writes often emit events. If they do, scan the historical logs to recover mapping keys, such as recipients from Transfer events. Remove duplicates, and pass the list to 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 or entities off-chain. Then pass the resulting list of addresses or keys to the on-chain batch clearing. This moves the enumeration complexity off-chain, and the on-chain work only applies the clears.

Brute-force via direct storage reads

If you have a candidate set of keys, such as all addresses that ever interacted with the contract, compute each mapping slot (keccak256(key, slot)). Tools such as cast index do this for you. Read the storage through RPC, and keep only the non-zero keys to clear.
This approach is tedious and RPC-heavy, but it works when events are missing or unreliable.

Appendix A - External cleaner contract

Appendix B - Proxy/upgradeable contract