---
title: "Transaction Tracker"
url: "https://radix-wiki.miow.me/contents/tech/core-concepts/transaction-tracker"
updated: 2026-10-03
description: "The Transaction Tracker is a system-level native blueprint that records the status of every transaction submitted to the network – including its intent hash…"
---

# Transaction Tracker

| Layer | Native [Blueprint](/contents/tech/core-concepts/blueprints-and-packages) |
| --- | --- |
| Blueprint name | `TransactionTracker` |
| Structure | Ring buffer of substate partitions |
| Epochs per partition | 100 |
| Coverage | ≈65 days of intent hashes |
| Created by | Protocol only (system execution) |
| Defined in | `radix-engine (transaction_tracker package)` |
| Source | [radixdlt-scrypto](https://github.com/radixdlt/radixdlt-scrypto) |

## Introduction

The Transaction Tracker is a system-level native [blueprint](/contents/tech/core-concepts/blueprints-and-packages) 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](/contents/tech/core-protocols/transaction-manifests) expiry windows, and provide deterministic transaction-status lookups via the [Gateway API](/contents/tech/core-protocols/radix-gateway-api).

## What it records

Every transaction submitted to Radix carries an _intent hash_ — a commitment to the [manifest](/contents/tech/core-protocols/transaction-manifests) 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](/contents/tech/core-protocols/radix-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](https://github.com/radixdlt/radixdlt-scrypto/blob/main/radix-engine/src/blueprints/transaction_tracker/package.rs) reserves every [substate partition](/contents/tech/core-concepts/substate-model) 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](/contents/tech/core-concepts/consensus-manager), 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](/contents/tech/core-concepts/badges) can make another. Writes into it come from the [transaction processor](/contents/tech/core-concepts/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](/contents/tech/releases/protocol-updates) without a migration of the records themselves.

## External Links

- [transaction_tracker native package (radixdlt-scrypto)](https://github.com/radixdlt/radixdlt-scrypto/tree/main/radix-engine/src/blueprints/transaction_tracker)
- [package.rs — ring buffer and coverage test](https://github.com/radixdlt/radixdlt-scrypto/blob/main/radix-engine/src/blueprints/transaction_tracker/package.rs)
- [transaction validation configuration — `max_epoch_range`](https://github.com/radixdlt/radixdlt-scrypto/blob/main/radix-transactions/src/validation/transaction_validation_configuration.rs)
