---
id: TIP-1046
title: T4 Hardfork Meta TIP
description: Meta TIP collecting all bug fixes and security hardening changes gated behind the T4 hardfork.
authors: Federico Gimenez (@fgimenez), Derek Cofausper (@decofe), Tanishk Goyal (@legion2002), Marc Martinez (@0xrusowsky), Arsenii Kulikov (@klkvr)
status: Draft
related: TIP-1011, TIP-1016, TIP-1031, TIP-1038
protocolVersion: T4
---

# TIP-1046: T4 Hardfork Meta TIP

## Abstract

This meta TIP collects bug fixes and security hardening changes that activate at T4. Each item is small in isolation, but together they define the complete in-scope T4 bug-fix bundle. Fixes already specified by other standalone TIPs are intentionally excluded from this meta TIP.

## Motivation

Ongoing internal review and audit follow-ups uncovered correctness and security issues that require hardfork gating. Because these fixes alter state-function behavior at activation boundaries, they are grouped here as one coordinated rollout under T4.

---

# Changes

## 1. Check recipient authorization on payout token in `cancel_stale_order`

**PR**: [#3181](https://github.com/tempoxyz/tempo/pull/3181) · **Author**: @fgimenez

`cancel_stale_order` returns funds to the maker but did not verify that the maker is still authorized as a recipient on the payout token. Orders from makers blacklisted as recipients could not be cleaned up by third parties. T4+ adds a recipient authorization check on the payout token so these orders can be cancelled.

## 2. Propagate OOG from `is_initialized()` instead of masking as uninitialized

**PR**: [#3535](https://github.com/tempoxyz/tempo/pull/3535) · **Author**: @decofe

TIP-20 dispatch previously called `is_initialized()` and then collapsed all errors into `false` via `.unwrap_or(false)`. That masked out-of-gas during the initialization check as `TIP20Error::uninitialized()`, changing the failure mode and gas semantics. T4+ preserves the old behavior before activation and propagates the real out-of-gas error after activation.

## 3. Key-auth scope surcharge

**PRs**: [#3595](https://github.com/tempoxyz/tempo/pull/3595), [#3689](https://github.com/tempoxyz/tempo/pull/3689) · **Authors**: @legion2002

Scoped key authorizations need additional intrinsic gas to cover the helper bookkeeping around scope persistence. T4+ applies the rounded scope surcharge described in [TIP-1011](./tip-1011).

## 4. Scope set length reset pricing

**PR**: [#3680](https://github.com/tempoxyz/tempo/pull/3680) · **Author**: @legion2002

Repeated target and selector scope-set length-slot writes need to be charged like warm resets instead of fresh `SSTORE_SET` rows. T4+ aligns those repeated length-slot updates with the actual storage-touch pattern used by scope persistence.

## 5. Empty-recipient delete optimization in selector-any-recipient scopes

**PRs**: [#3577](https://github.com/tempoxyz/tempo/pull/3577), [#3690](https://github.com/tempoxyz/tempo/pull/3690) · **Authors**: @legion2002

Selector rules with an empty recipient set represent allow-all recipients and do not need an extra `delete()` on the nested recipient set. T4+ skips that redundant storage touch in the keychain scope update path while preserving the same persisted allow-all semantics described in [TIP-1011](./tip-1011).

## 6. Check paused state on internal-balance DEX debit paths

**PR**: [#3586](https://github.com/tempoxyz/tempo/pull/3586) · **Author**: @decofe

Order placement and `place_flip` can consume existing internal DEX balances without calling TIP-20 `transferFrom()`. That means the usual paused-token check on token transfer never runs when internal balance alone covers the debit. T4+ adds an explicit pause check on those internal-balance-only debit paths so paused tokens cannot be used to place new orders or flips through pre-deposited balances.

## 7. Pre-charge gas before cold loads in storage and account access

**PR**: [#3763](https://github.com/tempoxyz/tempo/pull/3763) · **Author**: @0xrusowsky

Storage and account access currently deduct the static portion of gas after performing the cold load, which allows cheap state reads in cases that should fail up front on gas exhaustion. T4+ deducts the static read or `SSTORE` gas before touching cold account or storage state, then only charges the additional cold-load cost when enough gas remains for that part of the access.

## 8. Skip redundant SLOAD on packed-struct stores

**PR**: [#2976](https://github.com/tempoxyz/tempo/pull/2976) · **Author**: @0xrusowsky

When storing a struct with packed fields, the generated code reads the first packed slot with an `SLOAD` before writing it back, even though struct slot groups are owned exclusively by the struct and all declared packed fields are overwritten before commit. T4+ starts from zero for that first packed slot instead, removing one redundant `SLOAD` per packed-struct store while preserving the packed layout semantics.

## 9. Allow omitting empty subblocks metadata system transaction

**PR**: [#3746](https://github.com/tempoxyz/tempo/pull/3746) · **Author**: @klkvr 

Blocks currently always include a subblocks metadata system transaction, even when no subblocks are present. T4+ allows blocks to omit that empty metadata transaction, treats missing metadata as an empty subblocks list during execution, and rejects explicit subblock metadata or subblocks after activation so the post-T4 path consistently operates without subblocks.

## 10. Pre-validate call scopes before writing

**PR**: [#3793](https://github.com/tempoxyz/tempo/pull/3793) · **Author**: @klkvr

Key authorization logic previously wrote call scopes to storage one by one and validated each after insertion. A malicious batch could force the node to insert a large set of scopes where only the last entry fails validation, wasting storage writes and creating a DOS vector. T4+ also relaxes selector scope validation to check the target address without requiring TIP-20 initialization, matching the intended authorization semantics.
