Monero Subaddresses vs Payment IDs: What Still Works
In 1,463 recent Monero transactions, 92.8% carried something that looks like a payment ID and not one carried the old 32-byte kind. What a subaddress really is, why the padding exists, and the five ways receivers still get it wrong.
Roughly 93% of Monero transactions carry a payment ID, and almost none of them mean anything. Between blocks 3,791,102 and 3,791,141 on 24 October 2026 the chain carried 1,463 ordinary transactions. Of those, 1,357 (92.8%) contained a nonce in the extra field, and 1,355 of them were exactly 9 bytes long: a 0x01 marker followed by an 8-byte encrypted payment ID. Not one transaction carried the 32-byte long payment ID that exchanges handed out in 2017. The official Moneropedia says it plainly - long payment IDs were removed in release 0.15 and cannot be used any more - so what looks like a payment ID on chain is, in the overwhelming majority of cases, padding that wallets insert on purpose.
An address and a payment ID are two different objects, and confusing them causes most of the support tickets. A Monero address is 95 characters: a one-byte network prefix, a 32-byte spend key, a 32-byte view key, and a 4-byte checksum, base58-encoded in 8-byte blocks - eight blocks of 11 characters plus a 5-byte tail of 7, which is exactly where the 95 comes from. The prefix decides the first character. In cryptonote_config.h, standard addresses use prefix 18 and start with 4, subaddresses use 42 and start with 8, and integrated addresses use 19. An integrated address is the same payload plus an 8-byte payment ID baked in: 77 bytes, 106 characters, still starting with 4. A payment ID never lives inside an address (except in that integrated form); it rides along as metadata in the transaction's extra field.
A subaddress is one key addition away from the address you already have, not a second wallet. The spend key of subaddress (account, index) is the primary spend key plus a hash of the primary view key, the account index and the subaddress index; the view key is shared. Index 0 of account 0 *is* the primary address, which is why a wallet shows it as the first entry in the list. The index is a 32-bit integer, so one account can label 4,294,967,295 destinations without a second seed, a second backup or a second set of keys to lose. That is the whole trick behind giving every customer, every order or every invoice its own Monero address: the chain sees unrelated-looking destinations, and your wallet sees one account with labelled rows.
The padding is deliberate, and the source says so. In cryptonote_tx_utils.cpp the transaction builder starts with `bool add_dummy_payment_id = true;` and, when neither a long nor a short payment ID was supplied, encrypts a null hash8 and appends it - with the comment that this should end up being the vast majority of transactions as time goes on. Two things switch it off: the transaction already carries a payment ID, or it has more than the usual one destination plus change. The chain sample matches that rule almost exactly. Every one of the 1,357 nonce-bearing transactions had exactly two outputs, while 96 of the 106 without a nonce had three or more - 49 with three outputs, 16 with four, and one with sixteen. The ten exceptions were two-output transactions whose wallet simply did not pad.
The privacy effect cuts both ways, and that is the useful part. For a payer, padding is a win: a transaction that really does carry a payment ID cannot be told apart from one that does not, so the field stops leaking who is paying an exchange. For a receiver it removes a signal you may have relied on - you cannot look at the chain and decide whether the sender typed a payment ID or their wallet generated one. That is exactly why crediting has to key on the address, or better on a dedicated subaddress, rather than on the payment ID. If your accounting still joins deposits to customers by payment ID, you are matching on a field that is 93% noise.
Neither a subaddress nor an 8-byte payment ID costs extra, and the fee does not care. Across 326 transactions in the 10 blocks before 14:00 UTC on 24 October, the fee rate fell into three clusters: 20,000, 80,000 and 320,000 piconero per byte, taken by 179, 74 and 39 transactions. The median fee was 44,570,000 piconero - 0.00004457 XMR, about 0.027 USD at the XMR price of 597.78 on CoinGecko. A one-input, two-output transaction measured 1,534 bytes and a two-input, two-output one 2,220. A subaddress output is an ordinary output: same tagged key, same 38-odd bytes, no surcharge. An 8-byte payment ID adds nine bytes to tx_extra, but the wallet pads that field anyway, so switching it on changes the size by nothing at all. The only real variable is the priority tier your wallet picks, and that is a 16x spread on the same transaction. (how Monero fees work)
On a swap order, this narrows to one rule: paste 95 characters and nothing else. Our order form asks for a single XMR address - the currency catalogue marks xmr as hasExtraId: false, so there is no tag field to fill and no payment ID to invent. The order API only checks that the address is a string under 256 characters, which means a 106-character integrated address would also pass validation and work on chain; it is simply not what the order asks for, and it is the kind of difference that sends a deposit into manual review. At 14:05 UTC on 24 October the quote endpoint returned 0.16382091 XMR for 100 USDT-TRC20, 0.82593 for 500, 1.652442 for 1,000 and 8.264534 for 5,000, while 1 XMR paid 592.31 USDT. If a withdrawal form at an exchange asks for a Payment ID while you are sending to us, leave it empty: an ID that we never asked for cannot help matching, and a 32-byte one cannot even be broadcast. (XMR to USDT exit)
Five ways receivers still get this wrong. One: treating the payment ID as mandatory, then refusing a deposit that arrived without one - the funds are in the address, and the missing field is not a missing payment. Two: pasting a 106-character integrated address into a form that documents 95, which some providers reject outright. Three: publishing one subaddress for everything, which rebuilds the shared-address problem the subaddress was supposed to solve. Four: batching payouts to an integrated address - the encrypted payment ID path requires exactly one destination, and the builder in cryptonote_tx_utils.cpp errors out with destinations that have more than one output. Five: assuming a new subaddress hides the amount or the timing, which it does not; it separates your customers from each other, nothing more.
Operating subaddresses properly takes three habits. Label at creation, not later: a wallet with 200 unlabelled rows is a wallet you cannot reconcile. Keep the refund address on the same seed but a different subaddress, so a failed order never returns to a customer-visible address. And use the view key, not the seed, for read-only accounting - a view key lets an accountant see incoming amounts without the ability to spend, which is the smallest exposure that still answers the question. On timing, the 40 blocks we sampled averaged 105.9 seconds between blocks against a 120-second target, so ten confirmations is roughly twenty minutes; that wait is the same whether the payout address is a primary address or subaddress 87 of account 3. (recovering misdirected funds)
The rule worth keeping. A payment ID is a label bolted onto a shared address; a subaddress is a label that is itself an address. In 2026 you want the second one everywhere you control the receiving side, and you want to leave the first one empty when someone else's form asks for it. Everything numeric above came from the chain or our own endpoints on 24 October 2026 around 14:00 UTC - 1,463 transactions, 326 fee samples, quotes at 592.31 USDT per XMR against a spot of 597.78 - and prices move, so the live quote on the swap page is the only number that binds. (XMR to USDT exit)