Introduction
The Transaction Tracker is a system-level native blueprint that records the status of every transaction submitted to the network – including its intent hash and expiry. It is what allows the network to detect and reject duplicate submissions, enforce transaction expiry windows, and provide deterministic transaction-status lookups via the Gateway API.
What it records
Every transaction submitted to Radix carries an intent hash — a commitment to the manifest and its header, including the epoch range in which the transaction is valid. The Transaction Tracker is the ledger's record of which intent hashes have already been seen, and with what outcome. Three statuses are stored: CommittedSuccess, CommittedFailure, and Cancelled.
Two guarantees rest on it. The first is that no intent can commit twice: a resubmitted transaction whose hash is already recorded is rejected outright, which is what lets a wallet safely retry a submission it never saw confirmed. The second is deterministic status lookup — when the Gateway API answers "did this transaction go through?", it is reading state every validator agrees on, not a mempool guess.
A ring of partitions
Storing every intent hash forever would grow without bound, so the tracker is a fixed-size ring. Its package definition reserves every substate partition from just above the main base partition up to 255, and assigns each one a window of EPOCHS_PER_PARTITION = 100 consecutive epochs. A transaction's record is filed in the partition covering its expiry epoch, computed by partition_for_expiry_epoch; an expiry outside the ring's current window has nowhere to go and the transaction is not accepted.
Every 100 epochs the tracker calls advance: the start epoch moves forward one window, the start partition steps on by one and wraps around at the end of the range, and the partition it lands on — the oldest — is cleared. Records age out by being overwritten rather than deleted individually, so the cost of the whole structure is bounded and constant.
The repository's own calculate_coverage test pins the size of that window: at the network's targeted five-minute epoch, the ring covers 65 days. The same test asserts the covered range is at least the transaction validator's max_epoch_range — that is the design constraint, and it runs in CI. How long a transaction may stay valid before it expires is not an independent parameter; it is bounded by how much history the tracker can hold.
System-owned, not user-callable
The tracker is a native blueprint but not an ordinary one. Its create function is gated behind system_execution(SystemExecution::Protocol), so only the protocol itself may instantiate it — there is exactly one tracker, created at genesis, and no blueprint or badge can make another. Writes into it come from the transaction processor as part of committing a transaction, not from a method call a manifest can make.
Its substate is versioned (TransactionTrackerSubstateV1) and stores only the ring's parameters — start epoch, start partition, the partition range, and the epochs-per-partition figure — so the layout can change in a future protocol update without a migration of the records themselves.
