EIP.tools

EIP.tools

โš ๏ธ DraftStandards Track: ERC

ERC-8003: ERC-20 Pre-initialization (Sentinel Storage)

Optional extension to pre-allocate a balance slot via a sentinel bytes32 value so the first SSTORE is prepaid, reducing later transfer gas.

Authors
Created2025-07-17
Discussion Linkhttps://ethereum-magicians.org/t/erc-8003-erc-20-pre-initialization-extension-sentinel-storage/24993
Requires

Markdown

https://raw.githubusercontent.com/ethereum/ERCs/re...
Pull Request#1158PR open

EIP-GPT summary

Contents
AbstractMotivationSpecificationBehavior:RationaleWhy This Extension?How It WorksJustification: Why Use bytes32 + Sentinel?Design Philosophy & ComparisonWhy Not Use Other Approaches?Broader ApplicabilityRisks and LimitationsBackwards CompatibilityReference ImplementationSecurity ConsiderationsCopyright

Abstract

This ERC-20 extension introduces an optional pre-initialization function for ERC-20 tokens, leveraging a sentinel (magic) value and a bytes32 storage mapping for balances. This mechanism allows users to pay the high SSTORE gas cost in advance (when gas is cheap), so that the later first real token transfer to that address costs far less gas during a potential upcoming network congestion.

Pre-initializing an ERC-20 token balance storage slot can save significant gas for users, but a simple approach (writing 0 to a uint256 balance) does not actually reduce costs for the eventual first real transfer at all. This extension proposes a safe, ERC-20-compatible method using mapping(address => bytes32) for balances and a unique sentinel value. The contract's ERC-20 interface remains 100% standard: all reads/writes are still performed as uint256. Only the internal pre-initialization function ever stores the sentinel value; all normal functions interpret it as zero, and any later transfer/mint simply overwrites it.

This allows users or integrators to "prepay" storage rent and optimize gas costs before high-demand launches, presales, and trending tokens. An easy way to understand for experienced developers is that this mirrors the principle of gas-shifting found in ERC-721A, but applied in reverse for ERC-20 storage.

Motivation

In Ethereum, the first write to a new storage slot costs 20000 gas (SSTORE from zero to nonzero). Pre-initializing by setting zero to a uint256 balance does not help, since the cost is incurred only when a nonzero value is first written. By using a sentinel (magic) value in a bytes32 mapping, contracts can explicitly allocate the storage slot for a given address, paying the high gas fee in advance, and later updating the slot with the real balance at only 5000 gas.

This makes gas costs predictable and manageable for scenarios where a spike in network fees is likely at time of launch, claim, or trending event.

Use cases (motivating examples):

  1. Presale Gas Hedging
    • During a presale or major launch, participants can pre-initialize their slot the night before, at low gas prices. Their eventual claim or buy is then cheaper.
  2. Strategic Pre-initialization for Trending Tokens
    • Savvy users can batch-initialize slots on tokens they may want to trade when hype spikes.
  3. Wallet/dApp Automation
    • Wallets, bots, and advanced dApps can offer a "pre-initialize now" button or automatic service for users to optimize their on-chain costs.

Specification

ERC-20 contracts implementing this extension MUST:

  • Store balances as mapping(address => bytes32).
  • Provide a public/external function:
    /// @notice Pre-initialize an address' balance slot with a sentinel value
    /// @param user The address to pre-initialize
    function preInitializeAddress(address user) external;

Behavior:

  • The function MUST store a unique, contract-wide constant sentinel value in the mapping if and only if the slot is currently empty.
  • The ERC-20 interface (balanceOf , etc.) MUST interpret this sentinel value as zero for all logic and external views.
  • Any subsequent mint/transfer to that address MUST overwrite the slot with the correct uint256 value.
  • No ERC-20 event emission or extra logic is required in preInitializeAddress.

Rationale

Why This Extension?

Ethereum's storage model makes the first write to any storage slot (SSTORE) disproportionately expensive: ~20,000 gas versus ~5,000 gas for subsequent updates. In traditional ERC-20, pre-initializing a balance slot with zero does NOT actually save gas for the user's first real balance update; only a nonzero value allocates the slot. This extension introduces a clean workaround: store a unique, non-numeric "magic" value in a bytes32 mapping to act as a sentinel, making the slot "allocated" and thus eligible for cheap update in the next transfer or mint.

Key insight:

  • You cannot "fake" a nonzero balance or use negative values (ERC-20 is uint256-only).
  • Storing a sentinel value using bytes32 allows the contract to "flag" the slot as pre-initialized without affecting the accounting logic or external ERC-20 compatibility.

This proposal addresses that with a simple, opt-in solution: pay the "storage rent" early, when it is cheapest.

How It Works

  • The contractโ€™s balance mapping is changed to mapping(address => bytes32).
  • The pre-initialization function stores a constant sentinel value (e.g. keccak256("preinit")) in the mapping for any address, only if itโ€™s currently zero.
  • When balanceOf() is called, or when transfers/mints are performed, if the current value is the sentinel, the contract logic treats it as zero (no tokens).
  • On the first real balance update, the sentinel is simply overwritten with the actual uint256 value. This update is now only ~5,000 gas.

Result:

  • Users (or wallets, or dApps) can pre-initialize their storage slot when gas is cheap.
  • When network is busy, their first actual token buy/claim/mint/transfer costs 17,096 less gas (by measurements done; see repository test results file in ariutokintumi/ERC-20-Pre-initialization/blob/main/testing/test_results.txt).

Justification: Why Use bytes32 + Sentinel?

  • Writing zero to a slot does not save gas for the first real (nonzero) balance write. EVM only discounts overwrites of already-allocated slots with nonzero value.
  • A nonzero, non-numeric "magic" value as a sentinel (e.g., bytes32(keccak256("preinit"))) can be easily recognized and replaced by the contract on the first real write.
  • The contract logic ensures full ERC-20 compatibility: all reads/writes are interpreted as uint256 externally; only the internal mapping uses bytes32.

Design Philosophy & Comparison

This extension shares a core design philosophy with ERC-721A, a widely adopted, optimized NFT standard that allows for cheap batch minting by deferring part of the gas cost to the first transfer of each token. ERC-721A takes a "low gas now, higher gas later" approach: Minting is cheaper for the user, but the first transfer of a token is slightly more expensive as the slot must be fully initialized then.

This proposal flips that paradigm: Here, we allow (but do not require) users to pay the "storage rent" up front, when gas is low, making their later transfers (when gas might be high) much cheaper. It's "high gas now, low gas later", the opposite time/gas bet than ERC-721A.

Just as with ERC-721A, neither approach reduces total gas in a flat-fee world. Both provide value by letting users/speculators decide when to pay expensive storage, aligning costs with their expectations of network activity and their own needs.

This makes the mechanism not just a technical optimization, but a user-experience and UX timing tool for advanced and power users, mirroring a pattern already accepted in the ERC-721 world.

Why Not Use Other Approaches?

  • Transfer(0) or balance=0: Does not allocate the slot; first nonzero write still costs 20000 gas.
  • Dummy balance (e.g. 1 token): Breaks accounting, is unsafe, and not compliant.
  • Negative or non-numeric: Not possible in uint256, and not safe in mapping types.

Sentinel bytes32 value is the only way to safely allocate the storage slot with zero external effects.

Broader Applicability

This mechanism could be generalized for any mapping that suffers high initial SSTORE costs (e.g. ERC-1155, allowances, etc).

Risks and Limitations

  • Slightly more complex contract logic (all reads must check for the sentinel).
  • "Storage bloat" possible, but no worse than any other pre-initialization or airdrop.
  • This approach is opt-in; only new contracts using this extension can benefit.

This extension is a safe, optional, and backward-compatible method to enable predictable gas costs for first-time token holders, especially during high-demand events, while preserving ERC-20 compatibility and preventing any accounting "mess" for token projects.

Backwards Compatibility

This function and pattern are optional and fully backward compatible: all ERC-20 views, transfers, and approvals remain unchanged in interface and behavior.

Reference Implementation

A minimal example contract is provided in the repository (github.com/ariutokintumi/ERC-20-Pre-initialization/tree/main/contracts). Don't use for production until a security audit is performed.

Security Considerations

  • No risk of funds loss: pre-initialization cannot set balances to nonzero token values or affect accounting.
  • The sentinel is never visible or accessible externally. ERC-20 APIs always interpret it as zero.
  • Minimal "storage bloat" risk; a slot with sentinel is no worse than a slot initialized via normal transfers.

Copyright and license: MIT

EIP.tools

EIP.tools

Search, read, and map Ethereum improvement proposals, ERCs, RIPs, and CAIPs from one focused interface.

Farcaster
by @apoorveth