Introduction
On most ledgers an address is an opaque hash: nothing in it tells you whether you are looking at a wallet, a token, or a contract, and nothing tells you which network it belongs to. A Radix address states both in plain text before you decode a single byte. account_rdx12... is an account on mainnet; resource_tdx_2_1t... is a token on Stokenet.
That is not cosmetic. The Bech32m human-readable prefix carries two independent facts β the entity type and the network β and the decoder cross-checks the prefix against the type byte inside the payload, so an address that claims to be one kind of thing and encodes another is rejected rather than misread. This page describes the format as the ledger actually implements it, citing radix-common/src/address rather than prose documentation.
Addresses are the values you put in a transaction manifest, and the Radix Engine Toolkit is what encodes and decodes them off-ledger.
Anatomy of an Address
Every global address is a Bech32m string in three parts:
account_rdx12y02de8ma5jz9pxkvgzr7g5v4g7hd44rvz4ynqvz3xy4cqe4vw6a2j
ββββββββ¬ββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββ
HRP β payload
separator- HRP β
<entity>_<network suffix>, assembled by HrpSet from the NetworkDefinition'shrp_suffix. There is one HRP per entity class per network. 1β the Bech32 separator. It cannot appear in the payload, so the last1in the string always marks the boundary.- Payload β the entity's node id in base-32, ending in a six-character checksum. The first decoded byte is the entity type discriminant.
AddressBech32Decoder performs three checks in order: the string must decode as Bech32m specifically (plain Bech32 is rejected with InvalidVariant), the first payload byte must map to a known EntityType, and the HRP actually present must equal the HRP that entity type is supposed to carry on this network β otherwise InvalidHrp. The last check is the one that matters for integrations: a well-formed address for the wrong network, or a resource address relabelled as a component, fails to decode rather than resolving to something unexpected.
Entity Prefixes
The HRP entity component is fixed per class of addressable thing. These are the global entities β the ones that appear in manifests and block explorers:
| Prefix | Entity | Type byte |
package_ | Package β published blueprint code | 13 |
resource_ | Resource manager β fungible (93) or non-fungible (154) | 93 / 154 |
component_ | Generic (Scrypto) component instance | 192 |
account_ | Account β allocated (193) or preallocated (209 / 81) | 193 / 209 / 81 |
identity_ | Identity β backs Personas | 194 / 210 / 82 |
validator_ | Validator | 131 |
consensusmanager_ | Consensus Manager β one per network | 134 |
transactiontracker_ | Transaction Tracker β one per network | 130 |
accesscontroller_ | Access Controller | 195 |
pool_ | Pool β one-, two-, or multi-resource | 196β198 |
locker_ | Account Locker | 104 |
Type bytes are the u8 discriminants of the EntityType enum. The source file notes that this table is mirrored in REP-60 and that prefix values are allocated under REP-71 β so the numbers are a deliberate, versioned assignment, not incidental ordering.
Why the First Payload Characters Are Readable
The discriminants are chosen so that the base-32 encoding of the type byte spells a mnemonic. Each payload character carries five bits: the entity byte's top five bits select the first character outright, and its bottom three bits constrain the second to one of four.
So GlobalValidator = 0b10000011 gives 10000 β s (system) and 011 β one of v, d, w, 0. Every validator address on the network therefore begins validator_rdx1s followed by one of those four characters β which holds for all 188 registered mainnet validators at epoch 330462. The families are:
pβ package Β·cβ standard global component (account, identity, access controller, pool) Β·dβ account lockersβ system component (consensus manager, validator, transaction tracker)tβ fungible ("token") resource and vault Β·nβ non-fungible resource and vault6β preallocated Secp256k1 entity Β·2β preallocated Ed25519 entitylβ internal component Β·kβ internal key-value store
The well-known native addresses go further and use vanity payloads, which is why the faucet reads component_rdx1cpt...faucet and XRD reads resource_rdx1tkn...radxrd. For ordinary entities only the first two characters are predictable; the rest is the node id.
Network Specifiers
The suffix after the entity name is the network's hrp_suffix, defined alongside its numeric id in NetworkDefinition:
| Network | Id | Suffix | Example |
| Mainnet | 1 | rdx | account_rdx1... |
| Stokenet | 2 | tdx_2_ | account_tdx_2_1... |
| Localnet | 240 | loc | account_loc1... |
Simulator (resim) | 242 | sim | account_sim1... |
The retired testnets keep their reservations β adapanet (10, tdx_a_), nebunet (11, tdx_b_), and the three RCnet generations kisharnet (12, tdx_c_), ansharnet (13, tdx_d_) and zabanet (14, tdx_e_) β so addresses captured from those networks still decode as historical artefacts and can never be confused with mainnet.
Telling networks apart. A recurring question is how to check whether a component or package is on Stokenet or mainnet. There is no on-ledger flag to query, because an address is only meaningful within the network that issued it: the suffix is the answer. Compare the network suffix rather than pattern-matching the whole prefix β account_rdx and account_tdx_2_ share the leading account_, and a naive startsWith on the entity name alone will match both. Decode the address instead and read the network back, which is what the Radix Engine Toolkit does; the Gateway you point at is itself network-specific, so a mismatched address simply will not resolve.
Global and Internal Entities
EntityType splits every entity into global and internal, and the HRP reflects the split. Global entities are independently addressable and can be called from a manifest. Internal entities are owned by exactly one parent and reached only through it, so they carry an internal_ HRP:
internal_vault_β a fungible or non-fungible vault inside a component or accountinternal_component_β a component instance owned by another componentinternal_keyvaluestore_β a key-value store, whose substates are treated as independent for contention and versioning
This is the addressing-level view of the substate model: a vault holding your XRD has a real address, but you cannot call it directly β you go through the account that owns it. Seeing an internal_vault_ address in a transaction receipt where you expected a resource_ address usually means you read a balance-change entry instead of the resource itself.
Preallocated Addresses
Most accounts on Radix were never created by a transaction. A preallocated (formerly "virtual") account or identity address is derived deterministically from a public key, so it is a valid destination before anything exists at it on-ledger; the account component is instantiated the first time it is used.
The signature scheme is part of the entity type, which makes it visible in the address:
GlobalPreallocatedEd25519Account(81) βaccount_rdx12...β the scheme the Radix Wallet uses for accounts created on BabylonGlobalPreallocatedSecp256k1Account(209) βaccount_rdx16...β including accounts carried over from OlympiaGlobalAccount(193) βaccount_rdx1c...β an account allocated by a transaction rather than derived from a key
The same three-way split applies to identities (82, 210, 194). The practical consequence: the second character of an account address tells you which curve secures it, before any ledger lookup. Deriving these addresses is a Radix Engine Toolkit function, not a Gateway call β it is pure computation over the public key.
Transaction Identifiers
The same HRP scheme covers the parts of a transaction, which is why a transaction id is self-describing in a log or an explorer URL:
txid_ | Transaction intent hash β the id you poll the Gateway with |
signedintent_ | Signed transaction intent |
notarizedtransaction_ | Notarized transaction β what is actually submitted |
subtxid_ | Subintent hash, introduced with Transaction V2 |
ledgertransaction_ | Ledger transaction hash |
roundupdatetransaction_ | Round update β consensus-generated, not user-submitted |
systemtransaction_ | System transaction, e.g. genesis and protocol updates |
These are hashes rather than addressable entities, so they carry no entity-type byte, but they take the network suffix β a txid_tdx_2_ is a Stokenet transaction. The distinction between the three intent-stage hashes matters when tracking a transaction: the intent hash is stable from the moment the intent is built, which is what makes it usable as an idempotency key. The full sequence is covered in Transaction Lifecycle.
Well-Known Addresses
Native entities are allocated fixed node ids in native_addresses.rs, so their addresses are constants you can hardcode. The payload is identical on every network; only the HRP changes:
| XRD | resource_rdx1tknxxxxxxxxxradxrdxxxxxxxxx009923554798xxxxxxxxxradxrd |
| Consensus Manager | consensusmanager_rdx1scxxxxxxxxxxcnsmgrxxxxxxxxx000999665565xxxxxxxxxcnsmgr |
| Transaction Tracker | transactiontracker_rdx1stxxxxxxxxxxtxtrakxxxxxxxxx006844685494xxxxxxxxxtxtrak |
| Faucet (testnets) | component_rdx1cptxxxxxxxxxfaucetxxxxxxxxx000527798379xxxxxxxxxfaucet |
| Account package | package_rdx1pkgxxxxxxxxxaccntxxxxxxxxxx000929625493xxxxxxxxxaccntx |
The XRD address above was confirmed against mainnet, not just read from source. The faucet is the clearest illustration of the network-suffix rule: the same component is component_tdx_2_1cptxxxxxxxxxfaucetxxxxxxxxx000527798379xxxxxxxxxfaucet on Stokenet, where it will actually dispense test XRD.
Alongside these sit the native badge resources whose non-fungible ids stand in for authority rather than assets β the virtual signature badges resource_rdx1nfxxxxxxxxxxed25sgxxxxxxxxx002236757237xxxxxxxxxed25sg and its secpsg counterpart, plus package, validator and account owner badges. They are ordinary badges as far as access rules are concerned, which is how "the signer of this transaction" becomes something a rule can name.
Working With Addresses
Three failure modes account for most address bugs in integrations:
- String matching instead of decoding. Checking
startsWith("account_rdx")looks safe until a Stokenet address arrives, and it tells you nothing about whether the checksum is valid. Decode and inspect the returnedEntityType. - Assuming Bech32. Radix uses Bech32m, and generic Bech32 libraries in encode mode will produce a checksum the decoder rejects. Confirm the variant your library emits.
- Treating a resource address as a component address. They are structurally interchangeable strings but different entity classes; the decoder will catch the swap, the Gateway will return nothing useful, and a manifest built on it fails static validation.
Static validation in the Radix Engine Toolkit catches all three locally, without a network round trip. For reading state at an address once you trust it, see Radix APIs; for putting addresses into transactions, see Transaction Manifest Language.
External Links
- hrpset.rs β the HRP set for every entity and transaction part
- entity_type.rs β EntityType discriminants and prefix derivations
- network/mod.rs β network ids and HRP suffixes
- decoder.rs β Bech32m validation and HRP cross-checking
- native_addresses.rs β well-known native entity addresses
- BIP-350 β Bech32m specification
- Radix Engine Toolkit β Radix Documentation
