---
id: TIP-1060
title: Storage Credits
description: Adds per-account reusable storage accounting. Every account earns a storage credit when it deletes one of its own storage slots and can apply that credit to a later storage creation. A new precompile lets each account read its balance and select between refund-based, preserve, and direct consumption modes.
authors: Dankrad Feist, Dragan Rakita
status: Draft
related: TIP-1000, TIP-1016, TIP-1058, TIP-1064
protocolVersion: T7
---

# TIP-1060: Storage Credits

## Abstract

This TIP introduces *storage credits*: per-account, non-transferable credits earned when an account deletes one of its own storage slots and later usable by that same account to offset storage creation costs. A storage credit covers the storage credit value (`STORAGE_CREDIT_VALUE = 245,000` gas) of TIP-1000's storage creation component (`SSTORE_CREATE_COST = 250,000` gas). The residual (`SSTORE_SET_COST = 5,000` gas) is charged by the normal SSTORE gas function only on clean creations, i.e. when `original == present == 0`.

This TIP preserves the existing SSTORE gas mechanism from EIP-2200, as amended by EIP-2929, EIP-3529, and TIP-1000, while making three protocol-wide gas-accounting changes:

1. **Storage deletion refunds are replaced by credits.** The legacy nonzero-to-zero clearing refund (`SSTORE_CLEARS_SCHEDULE` from EIP-3529) is set to zero. Instead, every nonzero-to-zero storage transition mints one storage credit for the account whose storage was modified. Other baseline SSTORE refunds, such as restore-to-original refunds from EIP-2200's net gas metering, are preserved.

2. **The TIP-1000 storage creation cost is split.** TIP-1000's storage creation component (`SSTORE_CREATE_COST`, 250,000 gas) becomes a residual (`SSTORE_SET_COST`, 5,000 gas) plus a creditable portion (`STORAGE_CREDIT_VALUE`, 245,000 gas). The residual is charged by the SSTORE gas function on clean creations only. The creditable portion is either paid as gas or covered by consuming one storage credit, depending on the account's selected mode.

3. **The EIP-3529 refund cap is removed.** The EIP-3529 limit that caps gas refunds to one fifth of gas used no longer applies. Storage-credit settlement refunds and preserved baseline SSTORE refunds are credited to the transaction's refund counter in full.

A new Storage Credits precompile lets each account read its credit balance and choose how credits are used for its own storage creations. `Refund` mode is the default: creations pay `STORAGE_CREDIT_VALUE` upfront, and credits are matched at end-of-transaction settlement. This lets credits minted later in the same transaction refund earlier creations while keeping the transaction's required gas limit independent of the account's credit balance at inclusion time. `Preserve` mode does not consume credits, and `Direct` mode consumes available credits synchronously, optionally bounded by a transaction-local budget.

The mechanism applies uniformly to every account on Tempo, including smart contracts, precompile-owned state, and EOAs. The selected mode, Direct budget, and pending refund count are transient and reset every transaction; only the per-account credit balance is persistent.

## Motivation

TIP-1000 makes net-new storage creation expensive to protect Tempo from state growth attacks. That cost is appropriate when an account increases its long-term state footprint, but it overcharges accounts that repeatedly delete and recreate storage slots while staying within a previously paid state footprint. Examples include AMM orderbooks that constantly add and cancel orders, channel-style precompiles that open and close escrow slots, using permit in a transaction that uses up the entire allowance, and ordinary contracts implementing transient data structures.

---

# Specification

## Terminology

- **Account**: Any address that can own EVM storage, including ordinary smart contracts, precompiles, and externally owned accounts.
- **Storage credit balance**: A protocol-maintained `uint64` counter associated with one account, holding the number of unspent storage credits for that account.
- **Storage creation**: A storage slot transition from zero to nonzero in storage owned by some account.
- **Storage deletion**: A storage slot transition from nonzero to zero in storage owned by some account.
- **TIP-1000 storage creation cost**: The state-creation component charged by TIP-1000 (`SSTORE_CREATE_COST = 250,000` gas), separate from the EIP-2929 access cost (2,100 gas cold, 100 gas warm) and any other base SSTORE gas. This TIP splits that component into two portions with different charging rules:
  - the **residual** (`SSTORE_SET_COST = 5,000` gas), charged by the SSTORE gas function itself — i.e. only on a *clean* creation where the transaction-original value is also zero (`original == present == 0`). On a dirty recreation (the slot was nonzero at the start of the transaction, e.g. `X -> 0 -> Y`), the SSTORE gas function charges no residual; and
  - the **creditable portion** (`STORAGE_CREDIT_VALUE = SSTORE_CREATE_COST - SSTORE_SET_COST = 245,000` gas), governed entirely by the storage credit mechanism on the present-to-new transition, independent of the original value.

  References elsewhere in this TIP to "the TIP-1000 storage creation cost", "the TIP-1000 creation cost", or "the TIP-1000 storage creation component" denote `SSTORE_CREATE_COST` as a whole. The EIP-2929 and base SSTORE gas remain payable per the SSTORE gas function.
- **Storage credit value**: The fixed amount (`STORAGE_CREDIT_VALUE`) that a single storage credit offsets — the entire creditable portion of the TIP-1000 creation cost. When a credit is applied to a clean creation, `SSTORE_SET_COST` still remains payable, so the credit reduces that creation's TIP-1000 component from `SSTORE_CREATE_COST` to `SSTORE_SET_COST`. When a credit is applied to a dirty recreation, the residual was not charged in the first place (per the rule above), so the credit reduces the creditable portion to zero with no residual.
- **Storage creation mode**: A per-account selection of one of the three modes below (`Refund`, `Preserve`, or `Direct`), held in transient storage. It is *not* persisted across transactions: it defaults to `Refund` at the start of every transaction, is cleared at end-of-transaction, and is journaled with EVM scopes (so a `setMode` or `setBudget` write reverts with the call frame that made it, per EIP-1153 transient storage semantics).
- **Direct-spend budget**: A per-account transaction-local `uint64` counter associated with `Direct` mode. It caps how many credits may be consumed synchronously by `Direct`-mode storage creations after an explicit `setBudget` call. The budget defaults to zero at the start of every transaction and is ignored unless the account is in `Direct` mode. Calling `setMode(Direct)` sets the budget to `type(uint64).max`, which is treated as effectively unlimited. Calling `setBudget(budget)` selects `Direct` and sets the remaining budget to `budget`, including `setBudget(0)`. A zero Direct budget is observable through `modeOf(account) == Direct` and `budgetOf(account) == 0`; storage creations while the budget is zero charge `STORAGE_CREDIT_VALUE` as gas and leave the credit balance unchanged, but do not change the selected mode. When a storage credit is consumed in `Direct` mode, the budget is decremented by one unless it is already `type(uint64).max`. If the budget reaches zero after a `Direct`-mode consumption, the selected mode remains `Direct`; later creations continue to charge as gas unless the budget is increased by another non-reverted `setBudget` or `setMode(Direct)` call.
- **Refund-credit mode**: The default storage creation mode. Each storage creation pays `STORAGE_CREDIT_VALUE` upfront as gas (in addition to `SSTORE_SET_COST` whenever the SSTORE gas function charges it) and increments a transaction-local *pending refund-eligible creation* counter for the storage-owning account. At end-of-transaction, the protocol matches each pending refund-eligible creation against the account's storage credit balance (as it stands at end-of-transaction, after all in-transaction deletions and mode changes) and credits a refund of `STORAGE_CREDIT_VALUE` per credit consumed.
- **Preserve-credit mode**: A storage creation mode in which storage creation always pays `STORAGE_CREDIT_VALUE` as gas (plus `SSTORE_SET_COST` when the SSTORE gas function charges it) and leaves the storage credit balance unchanged.
- **Direct-credit mode**: A storage creation mode in which, if a storage credit is available and the account's Direct budget is nonzero, one credit is consumed, the budget is decremented by one, and `STORAGE_CREDIT_VALUE` is covered by the credit rather than charged as gas. Any `SSTORE_SET_COST` charged by the SSTORE gas function (on a clean creation) is still paid.
- **Pending refund-eligible creation**: A `Refund`-mode storage creation that has not yet been redeemed for a refund. Counted per account, transaction-local, journaled with EVM scopes, and reset to zero at the start of each transaction.
- **Storage Credits precompile**: The protocol precompile, defined in [Storage Credits Precompile](#storage-credits-precompile), through which accounts read their balance and set their storage creation mode.

## Baseline SSTORE gas cost

This TIP builds on the existing SSTORE gas schedule (EIP-2200 as amended by EIP-2929,EIP-3529 and TIP-1000). That schedule is a function of three values for the slot being written:
- `original` (`O`) is the slot value at the start of the top-level transaction, before any writes made by that transaction. It is fixed for the transaction and does not change after earlier non-reverted writes.
- `present` (`P`) is the slot value immediately before the current SSTORE, after applying any earlier non-reverted writes in the same transaction.
- `new` (`N`) is the value being written by the current SSTORE.

By keying off all three, the gas schedule is able to only charge for the actual work imposed by the write — a no-op (`present == new`) costs a warm read, a clean creation (`original == present == 0`) costs the full set cost, a dirty rewrite of an already-touched slot costs only a warm read, and restoring a slot to its original value awards a matching refund so that within-transaction churn nets out to the work actually performed. This gas represent work done to update and write storage to database.

This baseline is left intact, with two exceptions: `SSTORE_CLEARS_SCHEDULE` is set to `0` (the clearing case is now handled by storage credits), and the clean-creation set cost is reduced to `SSTORE_SET_COST = 5,000` gas (4,900 dynamic plus the 100-gas warm charge), down from the legacy 20,000. The TIP-1000 net-new storage creation cost (`SSTORE_CREATE_COST`) is layered on top, split into `SSTORE_SET_COST` (charged on clean creations only) and `STORAGE_CREDIT_VALUE` covered by a storage credit or paid as additional gas. The baseline schedule is "work for this block"; the TIP-1000 component layered on creations represents long-term storage.

The table below enumerates every `(original, present, new)` transition class, the baseline EVM dynamic gas and refund it incurs (with `SSTORE_CLEARS_SCHEDULE = 0`), and how TIP-1060 classifies it:

> [!NOTE]
> The `EVM dyn gas` column excludes the 100-gas warm SSTORE/access charge that the opcode charges separately. Thus the clean-creation row lists 4,900 dynamic gas, which together with the 100-gas warm charge equals the `SSTORE_SET_COST` residual.

| O | P | N | Transition | EVM dyn gas | EVM refund | TIP-1060 class |
|---|---|---|------------|------------:|-----------:|----------------|
| 0 | 0 | 0 | no-op                     | 0      | 0      | none |
| 0 | Y | Y | no-op (same-tx)           | 0      | 0      | none |
| 0 | Y | Z | dirty overwrite           | 0      | 0      | none |
| X | X | X | no-op                     | 0      | 0      | none |
| X | X | Y | clean overwrite           | 2,800  | 0      | none |
| X | Y | X | dirty restore to original | 0      | 2,800  | none |
| X | Y | Z | dirty overwrite           | 0      | 0      | none |
| 0 | Y | 0 | clear of same-tx creation | 0      | 4,900 | **mint, credit +1** |
| X | X | 0 | clean clear               | 2,800  | 0      | **mint, credit +1** |
| X | Y | 0 | dirty clear               | 0      | 0      | **mint, credit +1** |
| 0 | 0 | Y | clean creation            | 4,900 | 0      | **creation, credit −1 or 245k gas** |
| X | 0 | X | recreation to original    | 0      | 2,800  | **creation, credit −1 or 245k gas** |
| X | 0 | Y | recreation to other value | 0      | 0      | **creation, credit −1 or 245k gas** |

A `nonzero -> 0` transition mints one credit irrespective of `O` and of mode (**credit +1**). A `0 -> nonzero` transition is a creation: `STORAGE_CREDIT_VALUE` is handled per mode (see [Storage Credit Consumption](#storage-credit-consumption-present--0-to-new--0)) — either consuming one credit (**credit −1**) or charging `STORAGE_CREDIT_VALUE` — while `SSTORE_SET_COST` is charged only on a clean creation (`original == present == 0`). Every other transition neither mints nor consumes a credit.

### Benchmark note

To validate that `SSTORE_SET_COST = 5,000` properly prices the state-write/root work of credited storage creation, the residual was checked with a DB-backed Reth/Tempo provider benchmark that exercises hashed storage/trie state, `state_root_with_updates`, provider hashed-post-state checks, and DB/trie commit smoke checks. The benchmark uses 5k slot updates in one transaction as an upper-bound case: this is slightly above the theoretical maximum number of credited clean creations that can fit under the production transaction gas cap once the 5k residual per slot and fixed transaction overhead are included. Each row compares `0 -> X` creations against comparable `X -> Y` updates, averaging three runs per scenario.

> [!NOTE]
> Benchmark host: Apple M4 Max, 36 GiB memory, macOS Tahoe 26.5.1.

| Pre-existing account non-zero slots | `X -> Y` storage updates : removals | `0 -> X` storage updates : removals | `X -> Y` ns/slot | `0 -> X` ns/slot | Relative `0 -> X` cost |
|------------------------------------:|----------------------------------:|----------------------------------:|-----------------:|-----------------:|-----------------------:|
| 0 | 397 : 397 | 397 : 0 | 476.092 | 369.912 | 0.777x |
| 10k | 1,317 : 1,317 | 1,318 : 651 | 1,184.140 | 1,073.815 | 0.907x |
| 100k | 3,780 : 3,780 | 3,781 : 3,401 | 3,908.699 | 3,817.203 | 0.977x |

Trie diagnostics show that both cases update the same or nearly the same number of storage trie nodes; `X -> Y` updates remove more old trie nodes, while `0 -> X` inserts remove fewer. The results support the 5k residual as the execution price for the state-write/root path, while the remaining `STORAGE_CREDIT_VALUE` prices long-term storage growth.

## State

### Persistent state

The protocol MUST maintain the following persistent state:

```text
storage_credit_state: mapping(address account => uint256 packed_state)
```

The packed state word uses the following layout:

```text
bits   0..63: storage_credit_balance (uint64)
bits  64..255: reserved for future hardfork-gated extensions
```

The storage creation mode is **not** part of this persistent word; it is transaction-local transient state (see [Transaction-local (transient) state](#transaction-local-transient-state)).

The default packed state for every account is zero, which means a zero balance. Implementations MAY omit explicit storage for accounts with the default packed state.

Reserved bits MUST be written as zero by this TIP. Future TIPs MAY assign reserved bits, but clients MUST NOT infer semantics from nonzero reserved bits unless those semantics have been activated by a hardfork.

Each account's `storage_credit_state` word is the only persistent slot this TIP introduces, and it is first written when the account mints its first storage credit, i.e., when it deletes one of its own nonzero storage slots. Writes whose storage owner is `STORAGE_CREDITS_ADDRESS`, including the first nonzero write that creates an account's `storage_credit_state` slot, MUST be journaled as ordinary protocol state but MUST NOT incur `SSTORE_CREATE_COST`. Implementations MUST only charge ordinary protocol gas for the backing state write, such as the non-creating update/reset cost when a value changes. These writes also MUST NOT be treated as TIP-1060 storage-credit events: they MUST NOT themselves mint or consume storage credits and MUST NOT increment `pending_refund_eligible_creations`. The same exclusion applies to protocol/system writes performed outside user-metered EVM execution, including pre-transaction and post-transaction fee accounting writes: they do not consume gas and MUST NOT participate in storage-credit minting, consumption, pending-refund accounting, or settlement.

This exemption is not a discount for arbitrary user storage. It waives only `SSTORE_CREATE_COST` for the protocol metadata slot that tracks an account's credit balance. That slot is only ever created as a side effect of the account deleting one of its own pre-existing nonzero storage slots — storage for which the account already paid the full TIP-1000 creation cost. An account therefore cannot create a credit-balance slot without simultaneously removing a previously paid slot, so the exemption cannot be used to grow net state cheaply: minting requires pre-existing paid storage to delete, and mass creation of credit-balance slots is bounded by the cost already paid for the slots being deleted. Because the storage creation mode and Direct budget live in transient storage, `setMode` and `setBudget` create no persistent slot and contribute nothing to this footprint.

### Protocol bookkeeping slots restored after user execution

Some protocol bookkeeping writes happen before or after user-metered EVM execution and therefore run outside TIP-1060 accounting. A slot that can be cleared during user execution and then recreated later by one of those disabled-accounting writes MUST NOT mint a storage credit when cleared during the same transaction, because the later recreation would not consume a credit or pay `STORAGE_CREDIT_VALUE`.

Implementations MUST treat deletions of the following transaction-specific slots as ineligible for TIP-1060 storage credits:

1. The current transaction fee payer's TIP-20 fee-token balance slot.
2. The current transaction access key's AccountKeychain spending-limit `remaining` slot for the fee token, when fee limits are enforced and the fee payer is the transaction caller.

This exclusion is transaction-local. It applies only to the concrete slots that pre/post-transaction fee collection or fee-limit bookkeeping may recreate with TIP-1060 disabled; unrelated slots owned by the same precompile or token remain governed by the normal minting and consumption rules.

Periodic AccountKeychain spending limits MUST also avoid clearing the `remaining` field when a period's allowance reaches zero on T7 and later. Implementations may represent zero remaining periodic allowance with a nonzero sentinel value, provided all reads and refunds decode the sentinel as zero and the sentinel cannot collide with a valid configured `remaining` value. This prevents an exhausted periodic limit from minting a stale AccountKeychain-owned credit that a later disabled-accounting fee pre-collection write could leave unbacked.

Fee distribution from TipFeeManager bookkeeping MUST run with TIP-1060 storage-credit minting disabled, the same as fee collection and fee refunds. Moving collected protocol fees is protocol accounting, not a user-metered storage deletion, and MUST NOT mint credits when it clears TipFeeManager-owned balance slots. The same minting exclusion applies to FeeManager swap-transfer bookkeeping that clears balance slots created for free during pre-transaction fee collection.

### Transaction-local (transient) state

The protocol MUST maintain the following transaction-local state in transient storage. The mapping is not persisted across transactions:

```text
storage_credit_transient_state:    mapping(address account => TransientState)

struct TransientState {
    uint64 budget;          // remaining direct-spend budget; default 0
    uint8 mode;             // 0=Refund, 1=Preserve, 2=Direct, 3=reserved
    uint64 pending_refunds; // pending refund-eligible creations; default 0
}
```

The mapping is initialized to its default values at the start of every transaction and discarded at end-of-transaction. `storage_credit_transient_state[account].mode` defaults to `Refund` (`0`), `storage_credit_transient_state[account].budget` defaults to zero, and `storage_credit_transient_state[account].pending_refunds` defaults to zero. All fields are journaled with EVM scopes — a write reverts with the call frame that made it under normal EVM revert semantics, exactly like EIP-1153 transient storage — and `pending_refunds` is consulted only by the [End-of-Transaction Settlement](#end-of-transaction-settlement) procedure.

The `storage_creation_mode` value `3` is reserved; clients MUST treat it as invalid until a future hardfork assigns it semantics, and `setMode` MUST reject it (see [precompile behavior](#behavior)).

For readability, the rest of this TIP refers to `storage_creation_mode[A]`, `storage_credit_budget[A]`, and `pending_refund_eligible_creations[A]` as aliases for `storage_credit_transient_state[A].mode`, `storage_credit_transient_state[A].budget`, and `storage_credit_transient_state[A].pending_refunds`.

## Storage Credit Minting (`present != 0` to `new == 0`)

This section specifies the **mint, credit +1** rows from [Baseline SSTORE Gas and Transition Classes](#baseline-sstore-gas-and-transition-classes). When account `A` changes one of `A`'s storage slots from nonzero to zero (the *present* value before the write is nonzero and the *new* value is zero), the protocol:

1. Applies the normal storage deletion gas behavior, except that the legacy storage-clearing refund is not credited (`SSTORE_CLEARS_SCHEDULE` is `0`).
2. Increments the `storage_credit_balance` field for `A` by one.

The slot's transaction-original value is irrelevant: a credit is minted on every nonzero-to-zero transition, including a within-transaction `0 -> X -> 0` clear of a slot created earlier in the same transaction. This is intentional and symmetric — a matching `0 -> X` creation consumes a credit (see [Storage Credit Consumption](#storage-credit-consumption-present--0-to-new--0)), so churn nets out and credits cannot be farmed without first creating (and paying for) the storage being cleared.

Any increment in the minting procedure above MUST be atomic with the storage write that caused it and MUST be journaled with the same EVM execution scope. If the storage write reverts under normal EVM revert semantics — because the frame containing it reverts, runs out of gas, or is unwound by a parent frame's revert — the storage credit mint MUST also revert, restoring the prior `storage_credit_balance[A]` value. An account therefore cannot accrue a storage credit without leaving a corresponding persisted nonzero-to-zero transition at end-of-transaction: a credit and its underlying deletion either both persist or both unwind.

Deleting a slot mints exactly one storage credit regardless of the deleted value, the slot key, the transaction sender, or the call path that caused the deletion. The credit is credited to the account (generally, the contract or precompile) whose storage was modified — the account whose storage the SSTORE actually wrote, per normal EVM semantics.

If `storage_credit_balance[A] == type(uint64).max`, deleting another slot MUST NOT overflow the counter. The balance remains saturated at `type(uint64).max`, and the deletion otherwise proceeds normally.

## Storage Credit Consumption (`present == 0` to `new != 0`)

This section specifies the **creation, credit −1 or `STORAGE_CREDIT_VALUE` gas** rows from [Baseline SSTORE Gas and Transition Classes](#baseline-sstore-gas-and-transition-classes). When account `A` changes one of `A`'s storage slots from zero to nonzero (the *present* value before the write is zero and the *new* value is nonzero), the write is a storage creation for TIP-1060 purposes. The slot's transaction-original value is irrelevant to credit accounting: the credit mechanism keys only off the present-to-new transition. Tracking the transaction-original value would be incorrect because a creation of slot A can be funded by a credit minted by clearing an unrelated slot B; the two are not paired by slot. Every zero-to-nonzero transition is subject to credit handling, and every nonzero-to-zero transition mints one credit, so within-transaction recreations such as `X -> 0 -> Y` correctly mint on the `X -> 0` step and apply credit handling on the `0 -> Y` step.

The present/new model governs only `STORAGE_CREDIT_VALUE`. `SSTORE_SET_COST` is charged by the SSTORE gas function and therefore only on a clean creation where the transaction-original value is also zero (`original == present == 0`); on a dirty recreation (`X -> 0 -> Y`) it is not charged. The mode only determines how `STORAGE_CREDIT_VALUE` is handled, by selecting exactly one of the following branches based on `storage_creation_mode[A]`:

1. `storage_creation_mode[A] == Refund`:
   - Charge `STORAGE_CREDIT_VALUE` as gas upfront.
   - Increment `pending_refund_eligible_creations[A]` by one (transaction-local, journaled with the current EVM scope).
   - Do NOT decrement `storage_credit_balance[A]` and do NOT credit a gas refund at this point. Credit consumption and the resulting refund happen during end-of-transaction settlement.
2. `storage_creation_mode[A] == Preserve`:
   - Leave `storage_credit_balance[A]` unchanged.
   - Charge `STORAGE_CREDIT_VALUE` as gas.
3. `storage_creation_mode[A] == Direct` and `storage_credit_balance[A] > 0` and `storage_credit_budget[A] > 0`:
   - Decrement `storage_credit_balance[A]` by one immediately.
   - Decrement `storage_credit_budget[A]` by one unless it is already `type(uint64).max`.
   - Cover `STORAGE_CREDIT_VALUE` with the consumed credit instead of charging it as gas.
4. `storage_creation_mode[A] == Direct` and (`storage_credit_balance[A] == 0` or `storage_credit_budget[A] == 0`):
   - Leave `storage_credit_balance[A]` and `storage_creation_mode[A]` unchanged.
   - Charge `STORAGE_CREDIT_VALUE` as gas.

In every branch, `SSTORE_SET_COST` is charged independently by the SSTORE gas function (clean creation only), in addition to whatever the branch does with `STORAGE_CREDIT_VALUE`.

The synchronous parts (storage write, `Direct`-mode credit debit, `Refund`-mode pending counter increment, and Direct budget decrement) MUST be atomic with the storage write that caused them. If the storage write reverts, all of the above MUST revert according to normal EVM revert semantics.

### End-of-Transaction Settlement

After all EVM execution for the transaction has completed (after the top-level call frame returns) and before the transaction's final gas accounting is computed, the protocol MUST settle `Refund`-mode creations for every account `A` with `pending_refund_eligible_creations[A] > 0`:

1. Let `settled := min(pending_refund_eligible_creations[A], storage_credit_balance[A])`.
2. Decrement `storage_credit_balance[A]` by `settled`.
3. Add `settled * STORAGE_CREDIT_VALUE` gas to the transaction's gas refund counter (one storage credit value per redeemed creation). Only `STORAGE_CREDIT_VALUE` is refunded; `SSTORE_SET_COST`, where the SSTORE gas function charged it (clean creations), is not.

`pending_refund_eligible_creations[A]` is then discarded with the transaction. The order in which accounts are settled MUST NOT affect any account's outcome, because settlement is per-account and storage credits cannot move between accounts.

Settlement uses `storage_credit_balance[A]` and `pending_refund_eligible_creations[A]` as they stand at end-of-transaction, after all journaling has resolved. Credits minted by storage deletions earlier in the same transaction are therefore available to fund refunds for earlier `Refund`-mode creations in the same transaction. A transaction that creates storage and later (within the same transaction) deletes one of its own storage slots can receive a refund for the earlier creation against the just-minted credit, even if the account's pre-transaction balance was zero.

The standard EVM refund cap (the limit of one fifth, i.e. 20%, of the transaction's gas used per EIP-3529) is removed, so no cap applies to the transaction's gas refund counter. This is permissible because TIP-1060 removes the refund for storage cleared in a transaction (which was potentially subject to abuse), while adding a new type of refund for a user who paid full price for storage, when the contract was in REFUND mode and had an available gas credit that was consumed. This latter type of refund is not subject to abuse, because it only refunds gas that was paid for in the same transaction.```

If a `Refund`-mode creation occurred in a call frame that reverted, the increment to `pending_refund_eligible_creations[A]` is unwound by the journal before settlement and therefore does not produce a refund. Symmetrically, deletions in reverted scopes do not contribute to `storage_credit_balance[A]` at settlement time.

### Rationale for the default mode

`Direct` mode charges less gas upfront when a credit is available, but it creates a simulation-vs-inclusion failure mode: a transaction simulated against a state where `storage_credit_balance[A] > 0` may be included against a state where another transaction has already drained the balance, in which case the included transaction can run out of gas and revert even though the user's intent and signed gas limit were unchanged.

`Refund` mode avoids that by always charging `STORAGE_CREDIT_VALUE` as gas upfront. When credits are available at the end-of-transaction settlement, the user receives a `STORAGE_CREDIT_VALUE` refund per redeemed creation and pays the same net amount as `Direct` would (`SSTORE_SET_COST` on a clean creation, nothing extra on a recreation), but the worst-case upfront gas is bounded by the always-charged amount and does not depend on the credit balance at inclusion time.

`Direct` mode is available for accounts whose callers explicitly accept the simulation-vs-inclusion risk, for example because they reserve their own per-user storage credits inside the contract and only select `Direct` mode with a bounded Direct budget when their accounting guarantees that credits are reserved for the specific operation about to execute. The budget lets a contract cap protocol credit consumption for high-level operations whose number of underlying SSTORE creations varies with packing or data layout.

## Storage Credits Precompile

The protocol MUST deploy the Storage Credits precompile at the reserved address below that exposes the storage credit state to EVM callers and lets each account choose its storage creation mode.

### Precompile Address

```solidity
address constant STORAGE_CREDITS_ADDRESS = 0x1060000000000000000000000000000000000000;
```

Implementations MUST use this address consistently across consensus, RPC, and tooling. The address is reserved by this TIP; the final byte pattern is subject to confirmation during implementation review.

### Interface

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/// @title IStorageCredits - TIP-1060 precompile interface
/// @notice Per-account storage credit accounting. Each account earns a
///         credit when it deletes one of its own storage slots and can use
///         it to offset a later storage creation. Each account selects how
///         credits are applied to its own storage creations via `setMode`
///         or `setBudget`.
interface IStorageCredits {
    /// @notice Storage creation mode for an account. A single credit is worth
    ///         `STORAGE_CREDIT_VALUE` against the creditable portion of the
    ///         TIP-1000 creation cost; `SSTORE_SET_COST` is charged separately
    ///         by the SSTORE gas function on clean creations only.
    /// - `Refund`:   (default) `STORAGE_CREDIT_VALUE` is charged upfront; at
    ///               end-of-transaction, if a credit is available it is consumed
    ///               and a `STORAGE_CREDIT_VALUE` refund is credited.
    /// - `Preserve`: `STORAGE_CREDIT_VALUE` is always charged; credits are never
    ///               consumed.
    /// - `Direct`:   if a credit is available it is consumed and
    ///               `STORAGE_CREDIT_VALUE` is covered by the credit instead of
    ///               being charged as gas.
    enum Mode {
        Refund,
        Preserve,
        Direct
    }

    /// @notice Reverts if `setMode` is called with an undefined `Mode` value
    ///         (i.e. anything other than `Refund`, `Preserve`, or `Direct`).
    error InvalidMode();

    /// @notice Reverts if any function of this precompile is reached via
    ///         `DELEGATECALL` or `CALLCODE`. The precompile MUST be invoked
    ///         with a direct `CALL` (or `STATICCALL` for the view functions)
    ///         so that `msg.sender` is the actual calling account whose
    ///         storage the mode and credits govern.
    error DelegateCallNotAllowed();

    /// @notice Read the storage credit balance for `account`.
    /// @return The number of unspent credits for `account`. Saturates at
    ///         `type(uint64).max`.
    function balanceOf(address account) external view returns (uint64);

    /// @notice Read the storage creation mode for `account`.
    function modeOf(address account) external view returns (Mode);

    /// @notice Read the remaining transaction-local Direct budget for `account`.
    function budgetOf(address account) external view returns (uint64);

    /// @notice Set the storage creation mode for the calling account.
    /// @dev Applies to every subsequent zero-to-nonzero write to
    ///      `msg.sender`'s storage until another `setMode` call from the
    ///      same account. The change is journaled with the calling
    ///      execution scope and reverts with it under normal EVM revert
    ///      semantics. Any caller may set its own mode, including an EOA
    ///      (for which a mode simply has no effect, since it owns no storage).
    ///      Reverts with `DelegateCallNotAllowed` if reached via
    ///      `DELEGATECALL` or `CALLCODE`; the mode is keyed to the direct
    ///      caller, never a propagated `msg.sender`.
    /// @param newMode New storage creation mode. MUST be a defined `Mode`.
    function setMode(Mode newMode) external;

    /// @notice Set the calling account's transaction-local Direct-spend budget.
    /// @dev Switches the caller to `Direct` mode with that remaining budget.
    ///      If `creditBudget == 0`, the caller remains in `Direct` mode but
    ///      has no immediately spendable budget. The budget is decremented only
    ///      when a storage credit is actually consumed in `Direct` mode.
    /// @param creditBudget Maximum number of credits to spend directly after
    ///                     this call in the current transaction; zero selects
    ///                     `Direct` mode with no immediately spendable budget.
    function setBudget(uint64 creditBudget) external;
}
```

### Behavior

- All read functions MUST NOT modify state.
- `balanceOf(account)` returns the `storage_credit_balance` field of `storage_credit_state[account]`.
- `modeOf(account)` returns the current transient storage creation mode for `account` within the current transaction, which is `Refund` unless `account` has called `setMode` or `setBudget` earlier in the same transaction and that call has not been reverted. Exhausting a Direct budget does not change the selected mode.
- `budgetOf(account)` returns the current transient Direct budget for `account` within the current transaction. It is zero by default, `type(uint64).max` after a non-reverted `setMode(Direct)`, and the remaining budget after a non-reverted `setBudget` call and any subsequent `Direct`-mode consumption.
- Every function of the precompile MUST revert with `DelegateCallNotAllowed` if it is reached via `DELEGATECALL` or `CALLCODE`. Implementations MUST detect this the same way as other Tempo precompile guards: the call is non-direct whenever the executing-context address is not `STORAGE_CREDITS_ADDRESS` (equivalently, the precompile's own code is not the code being run). Only a direct `CALL` (or `STATICCALL` for the read-only functions) is permitted. This guarantees `msg.sender` is the actual calling account rather than a propagated ancestor, so a contract cannot use `DELEGATECALL` to the precompile to change a different account's mode.
- `setMode(newMode)` is callable by any account, including EOAs (and EIP-7702 delegated accounts). It is unauthenticated in the sense that any account may set its own mode but only its own; the direct caller (`msg.sender`) is the account whose mode is written. Setting a mode on an account that never performs its own storage writes (such as a plain EOA) simply has no effect.
- `setMode(newMode)` MUST revert with `InvalidMode` if `newMode` is the reserved value `3` (or any encoding outside `{0, 1, 2}`). Otherwise it MUST set the transient mode for `msg.sender` to `newMode` and set the transient Direct budget for `msg.sender` to `type(uint64).max` if `newMode == Direct` and zero otherwise. The writes target transient storage only: they create no persistent state, cost only ordinary transient-storage writes (EIP-1153 `TSTORE`-equivalent), MUST NOT incur a TIP-1000 storage creation charge, and MUST NOT mint storage credits, consume storage credits, or increment `pending_refund_eligible_creations`.
- `setBudget(creditBudget)` MUST set the transient Direct budget for `msg.sender` to `creditBudget` and MUST set the transient mode to `Direct`, including when `creditBudget == 0`. When `creditBudget == 0`, `modeOf(msg.sender)` reports `Direct`. Storage creations while the budget is zero follow the no-budget Direct branch: they charge `STORAGE_CREDIT_VALUE`, leave the credit balance unchanged, and leave the transient mode set to `Direct`.
- A contract that itself runs as the callee of a `DELEGATECALL` (i.e. library code executing in another account's context) may still call the precompile, because that is a direct `CALL` to the precompile from within that context: `msg.sender` is then the account whose code is executing and whose storage the mode governs. Only `DELEGATECALL`/`CALLCODE` *into the precompile itself* is rejected.
- A mode or budget write performed inside an execution scope that later reverts MUST itself revert, restoring the prior transient `storage_creation_mode` and `storage_credit_budget` values for the same account.

## Scope

This TIP applies to every account on Tempo. Storage credit minting, consumption, and mode selection follow the same rules whether the account is an ordinary smart contract, a precompile, or an EOA; any account may call `setMode` for itself. The effective SSTORE cost for accounts that never delete and re-create storage slots is unchanged, because their `storage_credit_balance` stays at zero and `Refund` mode degenerates to charging the full TIP-1000 cost with no refund.

## Compatibility

Existing contracts keep the default `Refund` mode and a zero balance until they delete one of their own existing storage slots, and do not need to interact with the Storage Credits precompile to benefit from the mechanism. Two protocol-wide changes do, however, alter storage gas economics for every account: the pre-existing storage-clearing gas refund (nonzero-to-zero) is removed (`SSTORE_CLEARS_SCHEDULE = 0`) and replaced by storage credit minting, and the standard EVM refund cap (EIP-3529) is removed, so the `Refund`-mode storage credit settlement and the preserved baseline refunds are credited to the gas refund counter in full.

For an account that deletes a slot and later creates one, this is neutral-to-favorable: instead of the small immediate clearing refund it previously received, it earns a storage credit that redeems (in the default `Refund` mode) for a `STORAGE_CREDIT_VALUE` refund at end-of-transaction settlement when it next creates a slot. The one downside is for a deletion that is *not* matched by a later creation in the same account: such an account no longer receives the immediate clearing refund it used to get, and instead holds a carried-forward credit that only yields gas savings if and when it later creates storage. Credits are non-expiring and account-keyed, so for slots that are cleared but never followed by a creation the loss is bounded by the former (small) clearing-refund amount. Removing the EVM refund cap is favorable in all cases.

Gas estimation for storage creations in the default `Refund` mode is unchanged in upfront cost: the same zero-to-nonzero write always charges the same upfront amount (`STORAGE_CREDIT_VALUE` plus, on a clean creation, `SSTORE_SET_COST`) and the required gas limit does not depend on the account's current credit balance. The effective cost is determined at end-of-transaction settlement and may be lower via a refund, including via credits minted by deletions earlier in the same transaction. Storage creations performed under `Direct` mode depend on whether a storage credit and nonzero Direct budget are available at the moment of the creation, so callers using `Direct` SHOULD assume the worst case (no credit or no Direct budget available) when sizing gas limits or accept that the transaction may fail if the balance changes between simulation and inclusion. Because the mode and Direct budget are transient and reset each transaction, a contract that relies on `Preserve`, `Direct`, or bounded Direct consumption MUST call `setMode` or `setBudget` in every transaction in which it wants that behavior; settings from one transaction do not carry over to the next. `setMode` and `setBudget` write only transient storage, so they cost ordinary transient-storage writes (EIP-1153), create no persistent state, and apply no `SSTORE_CREATE_COST` or storage-credit side effects.

### Interaction with TIP-1016

This TIP draws a clean line between two kinds of gas. The baseline SSTORE schedule (EIP-2200 as amended by EIP-3529) measures the *work this block performs* to apply a storage write — it accounts for original, present, and new values and is left intact (other than zeroing `SSTORE_CLEARS_SCHEDULE` and reducing the clean-creation set cost to `SSTORE_SET_COST = 5,000`). The gas this TIP layers on top of a creation — the TIP-1000 storage-creation component, now `SSTORE_SET_COST` plus `STORAGE_CREDIT_VALUE` — is not block work; it is payment for *long-term storage* and should count toward state gas.

[TIP-1016](./tip-1016.md) introduces a separate state-gas (reservoir) accounting dimension. If TIP-1016 is activated, its only effect on TIP-1060 is to reclassify `STORAGE_CREDIT_VALUE` for each storage creation as *consumed state gas* drawn from the reservoir, rather than as ordinary execution gas. Every other requirement in TIP-1060 stays exactly the same: storage credits are still minted on every nonzero-to-zero transition and consumed on every zero-to-nonzero creation, the `Refund` / `Preserve` / `Direct` modes behave identically, `SSTORE_SET_COST` is still charged as ordinary gas, and end-of-transaction settlement is unchanged. TIP-1016 does not change *whether* a credit is minted or consumed; it changes only the gas *dimension* `STORAGE_CREDIT_VALUE` is accounted against.

---

# Invariants

These invariants apply to storage operations governed by TIP-1060 and are evaluated over committed transaction effects after end-of-transaction settlement. Reverted execution scopes have no effect for invariant purposes.

For this section, a **backing unit** is an account-local entitlement corresponding to one `STORAGE_CREDIT_VALUE` creditable storage-creation portion that has been paid and has not been returned through any refund/refill path. A backing unit may be represented by live nonzero storage owned by the account or by one unit of `storage_credit_balance`.

1. **Credit backing and conservation:** For each account `A`, every unit counted in `storage_credit_balance[A]` MUST be backed by one unique backing unit for `A`. Backing units may move between live nonzero storage owned by `A` and `storage_credit_balance[A]`, and may be retired when the corresponding `STORAGE_CREDIT_VALUE` is refunded or when a saturated credit balance cannot represent another released unit. Backing units MUST NOT be created without a paid, non-returned `STORAGE_CREDIT_VALUE`, duplicated, transferred between accounts, or used to back more than one live slot or represented storage credit.

2. **Zero-crossing storage transitions preserve backing:** Every committed storage transition owned by account `A` from zero to nonzero MUST leave the resulting live slot backed by exactly one account-local backing unit, either by paying `STORAGE_CREDIT_VALUE` or by consuming an already-backed storage credit. Every committed transition from nonzero to zero releases the live slot's backing into at most one represented storage credit for `A`; if `storage_credit_balance[A]` is saturated, the backing may be discarded but MUST NOT be duplicated. Transitions that do not cross the zero/nonzero boundary do not create, consume, or release backing.

3. **Refund exclusivity:** `STORAGE_CREDIT_VALUE` may be returned only by consuming one backed storage credit through TIP-1060 settlement. After the return, the refunded payment no longer counts as backing; the consumed credit's backing replaces it wherever needed to satisfy live-storage backing for the same account, or is retired if no such backing is needed. The same `STORAGE_CREDIT_VALUE` payment MUST NOT both be returned as gas and leave behind a represented storage credit or additional live-slot backing. No other refund/refill mechanism may return `STORAGE_CREDIT_VALUE` for storage-credit-governed writes.

4. **Account locality:** Storage credits and their backing are keyed to the storage-owning account. A backing unit associated with account `A` may only back live storage owned by `A`, a represented storage credit of `A`, or a refund/offset for a storage creation owned by `A`.

5. **Protocol metadata and system-write exclusion:** Writes whose storage owner is `STORAGE_CREDITS_ADDRESS`, and protocol/system writes performed outside user-metered EVM execution, including pre-transaction and post-transaction fee accounting writes, MUST NOT incur the TIP-1000 storage-creation cost. These writes may record credit accounting required by this TIP, but their own storage-slot zero/nonzero transitions MUST NOT themselves be classified as storage-credit minting, consumption, or pending refund events.

6. **Direct-call authority for the precompile:** Every function of the Storage Credits precompile MUST revert with `DelegateCallNotAllowed` when reached via `DELEGATECALL` or `CALLCODE`. Consequently, `setMode` / `setBudget` can only update the transient state of the immediate direct caller of the precompile. The precompile MUST NOT be usable through delegated execution to modify state on behalf of a propagated, non-direct caller.
