Skip to content

Latest commit

 

History

History
56 lines (32 loc) · 4.36 KB

File metadata and controls

56 lines (32 loc) · 4.36 KB
id TIP-1038
title T3 Hardfork Meta TIP
description Meta TIP collecting all bug fixes, security hardening, and gas correctness changes gated behind the T3 hardfork.
authors Rusowsky (@0xrusowsky), Dragan Rakita (@rakita), Federico Gimenez (@fgimenez), Derek Cofausper (@decofe)
status Testnet
protocolVersion T3

TIP-1038: T3 Hardfork Meta TIP

Abstract

This meta TIP collects bug fixes, gas correctness improvements, and security hardening changes that activate at T3. Each item is small in isolation, but together they define the complete in-scope T3 bug-fix bundle.

Motivation

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


Changes

1. Skip redundant setUserToken write and event when token unchanged

PR: #3272 · Author: @fgimenez

setUserToken() unconditionally writes to storage and emits UserTokenSet even when the token hasn't changed. Since the event triggers a full O(pool_size) txpool scan via evict_invalidated_transactions, any account can force network-wide CPU work each block by repeatedly calling setUserToken with the same value. T3+ adds a read-before-write guard that returns early when the stored token already matches, eliminating the redundant write, event, and pool scan.

2. TIP-20: verify paused state before mint and burn

PR: #3411 · Author: @0xrusowsky

The TIP-20 pause mechanism was missing from mint, mintWithMemo, burn, burnWithMemo, and burnBlocked — an unintentional omission that left token-moving operations reachable while the contract was paused. The pause flag is meant to act as a universal kill switch; leaving mint/burn unguarded undermines that guarantee and, in particular, prevents the admin from stopping a compromised BURN_BLOCKED_ROLE key from seizing funds.

T3+ adds check_not_paused() to all five functions. A new validate_mint helper consolidates the pause check, recipient validation, and TIP-403 policy check into a single call. Administrative functions (role management, unpausing) and transferFeePostTx remain intentionally exempt.

3. Disambiguate optional AA expiry and validity timestamps

PRs: #3500, #3501 · Author: @legion2002

Several AA timestamp fields were encoded as Option<u64> in RLP, but None and Some(0) both serialize as the empty string. That made Some(0) silently roundtrip to None, which could invert user intent by turning an immediately expired access key or transaction into one with no expiry bound.

T3+ changes key_authorization.expiry, valid_before, and valid_after to Option<NonZeroU64> at the primitives layer so zero-valued bounds are unrepresentable. Serde-backed JSON/request deserialization rejects 0x0 explicitly for these fields, while downstream execution and pool components convert back to plain u64 only where comparisons or storage require it.

4. StablecoinDEX: check token paused in internal balance swaps

PR: #3204 · Author: @0xrusowsky

The StablecoinDEX swap_exact_amount_in and swap_exact_amount_out paths operate on internal DEX balances and bypass TIP-20 transfer, so the pause check in the token contract is never hit. A paused token could still be swapped through the DEX — including as an intermediate hop in a multi-leg route. T3+ adds check_not_paused() to validate_and_build_route for every token in the swap path, ensuring paused tokens block DEX swaps the same way they block direct transfers.

5. Account-keychain: clamp refunded spending limits to the configured max

PR: #3483 · Author: @rakita

refund_spending_limit() restored spending room with a saturating add, which could raise a T3 key's remaining allowance above the configured max during defensive refund paths. T3+ clamps refunded spending limits to the stored max, preserving the invariant that remaining <= max for T3 keys while leaving migrated pre-T3 rows on their legacy behavior because they do not persist a max bound.