Proposal: A Native Privacy System for Radix

Hi Radix fam, I played ping pong with Claude about privacy and here’s what I came up with. Feel free to comment, suggest or criticize. Spoiler: I’m unable to implement this myself, but I think it would be a great addition to the Radix stack.

Proposal: A Native Privacy System for Radix

Proposal for the Radix community
Status: Concept — Open for discussion


Introduction

Public blockchains like Radix offer total transparency by design. While this transparency is a strength for DeFi and network security, it represents a major barrier to adoption for everyday payments: any counterparty receiving a payment can view the sender’s entire balance and transaction history.

This document proposes a privacy system consisting of three complementary tools, built on Radix’s existing primitives, without compromising DeFi composability or exposing the network to disproportionate regulatory risks.

The three tools form a logical sequence:

  1. Stealth Addresses — hide the identities of parties in a payment

  2. Validator Level 1 — obtain XRD without traceable history, reserved for stakers

  3. Validator Level 2 — obtain XRD without traceable history, open to all

Terminology note: this document uses the term “validator” in accordance with the Radix ecosystem. A validator may also operate via a liquid staking contract aggregating multiple users — the described mechanism applies identically in both cases.


Tool 1 — Stealth Addresses

Problem solved

Today, when Alice pays Bob, Bob sees Alice’s address and can view her complete balance and full history. Conversely, Alice knows the stealth address to which she sent funds to Bob, and can monitor what he does with them afterwards. This is the equivalent of paying someone while giving them permanent access to your bank statement.

(The problem of post-payment surveillance by Alice is partially mitigated by Tool 2 through the natural mass of stakers, and definitively resolved by Tool 3 — see below.)

How it works

Bob publishes a unique stealth meta-address, composed of two public keys (see next section for integration with Radix Personas):

stealth_meta_address = (scan_public_key, spend_public_key)

For each payment, Alice generates an ephemeral random parameter and derives a unique, single-use ephemeral address. Bob scans the network with his private key to recognise addresses belonging to him. Each payment received by Bob arrives at a different address, with no apparent link between them.

Sender (Alice):
  1. Retrieves Bob's stealth_meta_address from his Persona
  2. Generates r, an ephemeral random parameter
  3. Computes: stealth_address = hash(r × scan_key) + spend_key
  4. Publishes r in clear in the transaction metadata
     (r is public — it reveals nothing without Bob's private key)
  5. Sends funds to stealth_address

Receiver (Bob):
  1. Scans recent transactions
  2. For each transaction, computes hash(r × scan_private_key)
  3. If match → funds recognised and added to balance
  4. Can spend with spend_private_key + hash(r × scan_private_key)

An observer sees a random address with no link to Bob, and a parameter r which, without Bob’s private scan key, yields no useful computation.

Integration with Personas

The stealth meta-address would be published as a standardised field in Persona metadata, already existing on Radix:

Persona (existing):
  identity_address  : "identity_1abc..."
  persona_data      : { name, email, ... }

Persona (extended):
  + stealth_meta_address : {
      scan_public_key  : "02abc...",
      spend_public_key : "03def...",
      version          : 1
    }

This field is public and visible to all — it reveals nothing about funds; it is solely a cryptographic key enabling the computation of payment addresses.

Separation of usage spheres

The system maintains a natural separation between two spheres:

DeFi sphere (transparent):
  Swaps, lending, staking, governance...
  Full composability preserved
  User accepts visibility

Payments sphere (stealth):
  Peer-to-peer payments
  Merchant payments
  Privacy by default

Stealth addresses are naturally incompatible with DeFi (smart contracts cannot manage changing addresses and bidirectional flows on ephemeral addresses), which constitutes an organic separation without requiring an explicit prohibition.

Required changes

At the Radix protocol level:

  • Standardisation of a stealth_meta_address field in Persona metadata (minor data schema modification)

At the wallet level:

  • Automatic generation of the stealth meta-address when creating a Persona

  • Automatic derivation of ephemeral stealth addresses when sending

  • Periodic background scan to detect incoming payments

  • Transparent aggregation of stealth balance for the user

  • Automatic selection of source addresses when spending (similar to UTXO management)

  • Automatic generation of a fresh stealth address for change

  • Transaction fee management via Radix’s native fee abstraction — fresh stealth addresses containing only received funds, fees must be handled by a third-party payer mechanism or included in the received amount

Important technical notes

Scanning and privacy: periodic scanning requires downloading recent transactions via a node. If Bob uses a third-party node, that node sees the tested transactions and could infer Bob’s stealth meta-address. For maximum privacy, using a personal node is recommended. This limitation is common to all existing stealth address systems.

Forward secrecy: since the stealth meta-address is permanently published in the Persona, a future compromise of the scan private key would allow retroactive identification of all received payments. Periodic rotation of the stealth meta-address in the Persona is recommended for users concerned about long-term privacy.


Level 0 — Standard Behaviour (Current Situation)

The validator operates in a fully transparent manner, with no modification from Radix’s current behaviour:

Staking rewards → staker's main wallet (visible on-chain)
All transactions → public and traceable
No obfuscation mechanism activated

This is the default behaviour of all current Radix validators. Levels 1 and 2 are additional options that the validator chooses to activate according to its policy and regulatory constraints. This choice is visible on-chain via the validator’s metadata.

Level 0 offers full compliance with all jurisdictions and requires no protocol or smart contract modification.


Tool 2 — Validator Level 1: Clean XRD for Stakers

Problem solved

Even with stealth addresses, a problem persists: funds have a traceable origin. If Alice receives XRD from an exchange or a known wallet, those XRD have a history. Moreover, Alice knows the stealth address to which she paid Bob — she can monitor what Bob does with it. Bob therefore needs to obtain XRD without a link to their previous history, to break this chain of traceability.

(This mechanism is accessible to the validator’s stakers. The case where Bob does not stake with this validator, or does not stake at all, is handled by Tool 3.)

How it works

Validators naturally host many stakers whose funds intermingle. This natural and involuntary mixing role can be formalised by allowing stakers to obtain “clean” XRD — without traceable history linked to their deposits.

Important note on the concept of “clean”: a clean XRD is not physically different from an ordinary XRD. “Clean” means that the traceability link between the origin of the funds and their destination has been broken by the obfuscation mechanism. Alice’s traced XRD can become Bob’s functionally clean XRD, because no observer can link the two.

Note on unstaking: withdrawing clean XRD is an entirely distinct operation from unstaking. Unstaking concerns the principal stake and is subject to Radix’s protocol delay (~1-2 weeks). Withdrawing clean XRD only concerns the rewards generated by that stake — these are not subject to the unstaking delay and are managed solely by the validator’s smart contract.

The mechanism relies on an NFT Claim Ticket:

Step 1 — Deposit
  Bob deposits X XRD (standard denomination) into the validator
  Bob generates S, a strictly confidential local secret, never published
  Bob also provides a personal random seed to the contract
  The contract records hash(S) — visible on-chain, but useless without S
  The contract computes the expiration delay:
    delay = hash(seed_bob + current_epoch) % (max - min) + min
  The contract issues an NFT Claim Ticket containing:
    - hash(S)
    - the denomination
    - the expiration epoch (random delay within the validator's range)
    - the validator identifier
    - Bob's stealth meta-address (for automatic sending at expiration)

Step 2 — Waiting (random delay imposed by the contract)
  The validator accumulates deposits from many stakers
  The delay, different for each deposit, breaks temporal correlation
  Nobody other than Bob knows his seed, so his exact delay is unpredictable

Step 3a — Active withdrawal from a fresh stealth address
  Bob presents S from a new never-used stealth address
  The contract verifies that hash(S) exists in its list
  The contract releases funds to this new address
  The NFT Claim Ticket is burned (single use)
  S is transmitted to the contract only for the duration of verification —
  it is never persisted in the contract's storage

Step 3b — Automatic expiration (if Bob does not act within the delay)
  The contract automatically derives a new stealth address
  from the stealth meta-address recorded at deposit
  Funds are sent to this fresh address
  Bob finds them at the next wallet scan, without any action on his part
  No traceable link is recreated to the original deposit address

On the expiration epoch

One epoch on Radix corresponds to approximately 5 minutes. The expiration delay is therefore expressed in number of epochs — for example a range of 2016 to 8640 epochs corresponds to between 7 and 30 days.

This delay differs for each deposit because it incorporates a seed provided by Bob at the time of deposit. Bob is the only person who knows this seed, so his exact delay is unpredictable to any external observer — which breaks temporal correlation between deposits and withdrawals.

The min/max range is defined in the smart contract. At Level 1, the operator can adjust it via an administration badge. At Level 2, it is set permanently at deployment and can no longer be modified — but seed-based variability remains fully intact within this range.

The automatic sending at expiration to a fresh stealth address (Step 3b) reduces the need to notify the user — funds are never lost, simply moved to an address that the wallet will recognise at the next scan.

The observer sees a deposit from one address, and a withdrawal to a different address with no apparent link. The secret S never appears in clear on the ledger.

Why an NFT rather than a badge

An NFT is a transferable object with no intrinsic link to an identity. Anyone who possesses the secret S can claim the funds — which reinforces anonymity since the contract cannot distinguish Bob from the original depositor. A badge, by contrast, is linked to an identity or status and would create a traceable link between deposit and withdrawal.

Contract security and auditability

The smart contract code is stored on-chain and executed identically by all validators. Any divergence in execution would be immediately detected by consensus. The contract can therefore be publicly audited by any developer before use, guaranteeing in particular that S is never persisted in storage and that funds are always released to the address specified by the caller.

The only theoretical residual attack surface is frontrunning: a malicious validator operating its own contract could see S in the parameters of a pending transaction and attempt to submit a competing transaction to appropriate the funds. This risk is strongly mitigated by three characteristics of Radix: finalisation in a few seconds leaves an extremely narrow attack window, the BFT consensus mechanism processes transactions in an orderly manner making opportunistic insertion very difficult, and the cost of the attack is high — the malicious validator must be precisely in the active validator set at the right moment, build a competing transaction within seconds with fees high enough to take priority, all for a unit gain limited to the amount of a single NFT Claim Ticket. It is nonetheless an additional reason for Level 2 to be designed as a fully autonomous smart contract, with no identifiable operator having privileged access to the mempool.

Fixed denominations

To avoid amount correlation, the validator only accepts standardised denominations:

10 / 50 / 100 / 500 / 1000 XRD

A deposit of 350 XRD would be decomposed into 3×100 + 1×50 — four separate operations with potentially different delays. If the desired amount cannot be decomposed exactly, the remainder is returned to the user’s wallet in their traced sphere — with no loss of privacy since those funds were already there.

Operator participation choice

Level 1 is optional for the validator. It chooses to activate this mechanism or not, according to its policy and regulatory constraints. This choice is visible on-chain via the validator’s metadata.

Positive ecosystem effects

This mechanism creates a virtuous cycle:

Wants clean XRD → must stake → XRD leave exchanges
→ less liquidity for sale → upward price pressure
→ more secure network → more attractive ecosystem

Privacy and network health pull in the same direction.

Regulatory argument

Level 1 is defensible before regulators (EU, FinCEN…) for several reasons:

  • Access is reserved for stakers, identifiable via their visible on-chain stake

  • The stake amount remains public — large fortunes are not invisible

  • A KYC entry point exists via the exchanges where XRD are initially purchased

  • The mechanism formalises something that already exists naturally in any active validator

Required changes

At the Radix protocol level:

  • Allow optional redirection of staking rewards to the validator’s smart contract, rather than to the staker’s main wallet

  • Standardisation of a metadata schema for validators indicating their participation level (0, 1 or 2)

At the wallet level:

  • Dedicated interface for deposit/withdrawal via the validator

  • Secure storage of secret S and seed in the wallet backup

  • Automatic management of delays and denominations

  • Distinction and tracking of clean XRD (from a validator) vs traced XRD (received directly)

  • Configurable accumulation threshold before sending to stealth address:

    • Default threshold: 10 XRD

    • Minimum threshold: 1 XRD (for small stakers)

    • Maximum threshold: 1000 XRD (for fewer addresses)

    • Maximum delay: 30 days (guaranteed receipt even if threshold not reached)

  • Configurable spending policy:

    • Clean priority — maximum protection, recommended for everyday payments

    • Automatic mixing — optimises fees by combining clean and traced

    • Manual selection — for advanced users

  • Contextual recommendation displayed by the wallet:

    • Clean XRD → “Ideal for everyday payments and peer-to-peer”

    • Traced XRD → “Recommended for DeFi, staking and governance”

At the smart contract level (without protocol modification):

The contract manages two distinct incoming flows, separately accounted for in a single vault:

Flow 1 — Redirected rewards (automatic):
  Arrive from the protocol at each epoch
  Accumulated internally until the staker-configured threshold
  Sent to fresh stealth address when threshold reached or max delay elapsed

Flow 2 — Voluntary staker deposits (Level 1 only):
  Sent manually by the validator's stakers
  Subject to random delay via NFT Claim Ticket
  Sent to fresh stealth address upon withdrawal

rewards_counter  : XRD from Flow 1
deposits_counter : XRD from Flow 2
total_clean_reserve : rewards_counter + deposits_counter

  • Nullifier list to prevent double use of a secret S

  • Queue management if liquidity is insufficient at withdrawal time

  • Automatic sending to a fresh stealth address at expiration (no intervention required from Bob)


Tool 3 — Validator Level 2: Clean XRD for Everyone

Problem solved

Level 1 requires staking with the relevant validator, which excludes users who do not stake or who stake elsewhere.

But there is a more fundamental problem that Level 1 only partially resolves: Bob cannot spend freely without being traced by Alice. Alice knows the stealth address to which she sent the funds. Even if Bob spends from it to a new stealth address, Alice sees that movement. For Bob to spend freely, he must first break this link — obtain XRD without a history linked to the address Alice knows. Level 1 offers this possibility to the validator’s stakers, but not to all users.

Level 2 resolves both problems simultaneously: it opens the obfuscation mechanism to any user without prior condition, allowing Bob to break the traceability chain regardless of his staking situation.

Differences from Level 1

The mechanism is identical — NFT Claim Ticket, secret S, random delay, fixed denominations — with two differences:

Level 1: only the validator's stakers can deposit
         anonymity set = validator's stakers (natural and legitimate)

Level 2: anyone can deposit traced XRD to obtain clean ones
         anonymity set = all users (broader, less controlled)

How to obtain an NFT Claim Ticket at Level 2

Step 1 — Bob sends traced XRD to the validator's contract
  (from any address, stealth or not)
  Bob generates S (local secret) and a random seed
  The contract records hash(S), computes the delay, issues the NFT Claim Ticket

Step 2 — Waiting for the random delay
  Meanwhile, other users' deposits accumulate
  The link between deposit and withdrawal becomes indiscernible in the mass

Step 3a — Active withdrawal
  Bob presents S from a new fresh stealth address
  The contract releases clean XRD, burns the NFT Claim Ticket

Step 3b — Automatic expiration
  If Bob does not act within the delay, funds are automatically sent
  to a fresh stealth address derived from his stealth meta-address

Internal accounting of the Level 2 validator

XRD in the vault are physically fungible, but the contract maintains three separate logical counters:

total_vault              : all XRD combined

rewards_counter          : XRD from staking (Flow 1)
staker_deposits_counter  : XRD deposited by stakers (Flow 2)
external_deposits_counter: XRD deposited by external users (Flow 3)

total_clean_reserve      : sum of the three counters

Priority rules and proportions

Stakers contribute to network security and validator liquidity — they benefit from priority on the available clean reserve. The recommended rule is a proportional allocation configurable by the validator at deployment:

70% of clean reserve → reserved for stakers (Flow 1 + Flow 2)
30% of clean reserve → available for external users (Flow 3)

This ratio is visible in the validator’s metadata before any deposit, so users know what to expect in terms of delays.

Tiered pricing by urgency

An external user can choose between two withdrawal modes, implemented as two distinct functions in the smart contract:

// Standard withdrawal — normal delay, base fees
fn standard_withdrawal(nft_claim: NonFungibleBucket) → Bucket

// Urgent withdrawal — reduced delay, increased fees
fn urgent_withdrawal(nft_claim: NonFungibleBucket, 
                     urgency_fee: Bucket) → Bucket

Urgency fees are paid directly to the validator, creating an additional economic incentive to activate Level 2. They also serve as a deterrent against Sybil attacks — a rushed attacker pays significantly more.

Stakers benefit from urgent mode at no extra cost, as an advantage of their contribution to the network.

Issues specific to Level 2

The Sybil attack is the main risk: an attacker fills the validator with their own deposits to monitor withdrawals by elimination. Possible mitigations are long and random delays, and deposit limits per address — but without KYC, these measures remain imperfect.

Legal liability: without an identifiable operator, the smart contract is autonomous, but its initial deployment is traceable. The operator is exposed to regulatory risks similar to those faced by Tornado Cash developers in certain jurisdictions.

Regulatory positioning

Level 2 remains more defensible than Monero or Zcash because:

  • Transactions remain pseudonymous, not anonymous

  • Deposits with the validator are visible on-chain transactions — there is an entry trace, even if the deposit/withdrawal link is broken

  • Exchange entry points into the ecosystem maintain an initial KYC point

  • Amounts are constrained by fixed denominations — very large sums require many visible operations

But it is clearly more exposed than Level 1 and should be operated as a fully autonomous smart contract to minimise risks for an identifiable operator.

Required changes

At the protocol level: no additional changes beyond Level 1.

At the smart contract level:

  • Three distinct incoming flows with separate accounting (rewards, staker deposits, external deposits)

  • No staking verification for Flow 3 — open to all

  • 70/30 priority rule configurable at deployment

  • Two withdrawal functions (standard and urgent) with tiered pricing

  • Delay range fixed permanently at deployment (non-modifiable)

  • Seed-based variability preserved within the range

  • Strengthened anti-Sybil mechanisms (longer delays, wider range)

  • Queue management if liquidity is insufficient

  • Designed for full autonomy without an identifiable operator


Overall Vision: The Complete Privacy Cycle

Alice wants to pay Bob anonymously:

[Alice]
Main wallet → Level 1 or 2 validator
→ Clean XRD on fresh stealth address

[Payment]
Alice's stealth address → Bob's stealth address
(Bob publishes his stealth meta-address via his Persona)

[Bob breaks post-payment traceability]
Received stealth address → Level 1 or 2 validator
→ Clean XRD on new stealth address

[Bob spends]
New stealth address → merchant's stealth address

At no step can an observer link Alice to Bob, nor Bob to his subsequent spending.


What the System Does Not Resolve (Honest Limitations)

System objective: protect the wealth and privacy of everyday users during daily payments. It is not intended to offer the total anonymity of Monero, but to prevent a merchant, peer, or casual observer from viewing a user’s balance and history from a single received transaction.

Amount correlation remains possible if validator traffic is low — hence the importance of adoption. Fixed denominations significantly mitigate this risk.

Visible stake at Level 1 reveals a range of staking income, even if spending remains private.

The first mile: the initial transfer from a public wallet to the first validator leaves a trace. This is unavoidable without deeper protocol modification.

Adoption is the condition for effectiveness: a validator with few users offers a weak anonymity set. The system protects better the more people use it. Level 2 is particularly exposed to this problem at launch — a bootstrapping strategy (economic incentives for early users, initial participation by large validators) would be necessary.

Sophisticated graph analysis: an analyst with additional off-chain data (IP addresses, network metadata, cross-referencing with external data) could potentially reconstruct certain links despite on-chain protections. The system resists naive analysis well, less so against advanced state-level or professional analysis.

Network (IP) protection: submitting a transaction reveals the sender’s IP address to the contacted nodes. For maximum privacy, using Tor or a VPN is recommended for stealth transactions. This point is beyond the scope of this proposal but must be known to users.


Summary of Required Changes

Change Level Where
stealth_meta_address field in Personas Fundamental Protocol
Redirection of rewards to validator’s smart contract Fundamental Protocol
Level metadata (0/1/2) for validators Minor Protocol
Scan, aggregation, derivation of stealth addresses Fundamental Wallet
Fee management via native fee abstraction Fundamental Wallet
NFT Claim Ticket and secret management Fundamental Wallet
Configurable reward accumulation threshold Fundamental Wallet
Clean / traced XRD distinction + spending policy Fundamental Wallet
Optional rotation of stealth meta-address Recommended Wallet
Scrypto contract validator Level 1 Level 1 Smart contract
Scrypto contract validator Level 2 (3 flows, priority, urgency) Level 2 Smart contract

Protocol modifications are surgical and well-delimited. The majority of the work resides in the wallet and smart contracts — meaning a large part of the system could be implemented by the community without waiting for a core protocol modification.


This document is a conceptual proposal open for discussion. Cryptographic and implementation details are intentionally simplified to facilitate understanding. A detailed technical specification would be the next step if the community receives these ideas favourably.

TL;DR — Native Privacy for Radix

Today, every Radix payment exposes your entire wallet balance and history to whoever receives it — like handing someone permanent access to your bank statement. This proposal fixes that for everyday payments, while keeping DeFi fully transparent.

The system has three optional, on-demand tools: Stealth Addresses ensure each payment goes to a fresh, unlinkable address — just like a credit card payment reveals nothing about your account balance. Validator Level 1 allows stakers to receive clean, history-free XRD from their own validator’s staking rewards. Validator Level 2 opens this to any user by letting them exchange traced XRD for clean ones through a validator acting as a mixer.

Everything is opt-in: users choose when to use privacy, validators choose whether to offer it. No transaction content is hidden from the network — funds remain pseudonymous, not anonymous — making this system far more regulatory-friendly than Monero or Zcash. Authorities retain traceability entry points via exchanges and visible stakes.

Most of the work lives in the wallet and smart contracts. Protocol changes are minimal and surgical.

1 Like

Potential improvements


1. Amount obfuscation

This is the most visible gap in the current system. Even with fixed denominations, an observer seeing “100 XRD in, 100 XRD out” can establish a correlation. Two approaches:

Confidential Transactions — encrypting amounts using Pedersen Commitments. The network can verify that inputs equal outputs without seeing the actual figures. This is what Monero uses. However it requires deep protocol modifications to Radix.

Automatic systematic fragmentation — more realistic short-term: the wallet automatically splits each payment into several sub-transactions of slightly random and variable amounts, making statistical correlation difficult without encryption. Less perfect but achievable without touching the protocol.


2. Temporal metadata obfuscation

We mentioned random delays, but we could go further with a scheduled transactions system: the wallet submits transactions at random moments within a window of several hours, regardless of when the user initiated the payment. An observer can no longer correlate “Alice opened her app at 2:32pm” with an on-chain transaction.


3. A stealth payment channel network

Inspired by Lightning Network but privacy-focused: off-chain payment channels between frequent users, where only net balances are settled on-chain periodically. For two people who pay each other regularly (employer/employee, couple, close friends), individual transactions never touch the blockchain.


4. Stealth addresses for non-XRD tokens

The system as described only covers XRD. But Radix supports thousands of tokens. Extending stealth addresses to any Radix resource would be a major improvement — paying in USDC, DeFi tokens, or any asset, with the same privacy guarantees.

This is technically feasible with the same mechanism, but multiplies wallet management complexity (scanning all possible resources) and fee handling (always paid in XRD via fee abstraction).


5. Proof of solvency without balance disclosure

A concrete use case: you want to prove to a DeFi lender that you have sufficient collateral, without revealing your exact balance. With ZK-proofs, you could prove “I have at least X XRD” without saying how much you actually hold.

This is the missing piece that would allow partial reconciliation of the stealth and DeFi spheres — without exposing your full wealth, you could still participate in certain protocols. This is what Aztec Network is exploring on Ethereum, but it is a multi-year undertaking.


6. Decentralised scanning

Currently, stealth address scanning relies either on a personal node (ideal but heavy) or a third-party node (convenient but which sees your queries). An oblivious scanning protocol would allow delegating the scan to a third-party service without that service knowing which addresses you are looking for — via Private Information Retrieval (PIR) techniques. This is active research in applied cryptography.


Priority assessment

Short term (achievable without protocol modification):
  ✅ Stealth addresses for all Radix tokens
  ✅ Automatic amount fragmentation at wallet level
  ✅ Random-timing transaction submission

Medium term (surgical protocol modification):
  ✅ What the document already describes

Long term (serious R&D required):
  ⏳ Confidential Transactions
  ⏳ ZK proof of solvency
  ⏳ Oblivious scanning

Extending to non-XRD tokens is probably the most impactful short-term improvement — it would immediately multiply the system’s utility for all DeFi users holding stablecoins or other assets.

A note on terminology: pseudofungible and superfungible tokens

In the original proposal, tokens whose traceability link has been broken are referred to as “clean” tokens. While intuitive, this term carries an unfortunate connotation — “clean money” in English implies the laundering of illicit funds, which is precisely the opposite of what is intended here.

Two more precise terms are proposed going forward:

Pseudofungible describes a standard Radix token today — fungible in value, but carrying a traceable history on the public ledger. The fungibility is real but incomplete.

Superfungible describes a token whose traceability link has been broken through the obfuscation mechanism. It recovers true and complete fungibility — indistinguishable from any other token of the same type, with no exploitable history.

The symmetry is intentional: pseudo (apparent fungibility) vs super (restored fungibility). Together they describe a spectrum rather than a binary, which more accurately reflects the reality of on-chain traceability.

The original proposal document retains the terms “clean” and “traced” for historical continuity, but pseudofungible and superfungible are considered the more appropriate denominations going forward.

Question (flightofthefox)

how’s derivation actually happening from the meta address. RE doesn’t have native derivation crypto currently. If happening off-chain - what does that infra look like

How derivation actually works

The stealth address derivation uses Elliptic Curve Diffie-Hellman (ECDH), the same cryptographic primitive already used by Radix for standard key operations. The math is:

Given Bob's stealth_meta_address = (scan_pubkey S, spend_pubkey P)
Alice picks a random ephemeral scalar r
Computes shared secret: ss = hash(r × S)
Derives stealth address: spend_pubkey + ss×G
Publishes r×G on-chain as the "hint"

Bob detects it by:
  Computing ss = hash(scan_privkey × (r×G))
  Checking if the derived address matches the tx destination

This is essentially EIP-5564 (Ethereum’s stealth address standard) adapted for Radix’s key scheme. The math requires only scalar multiplication on an elliptic curve — nothing exotic.

I guess fox you don’t need that, but for other readers: G is the generator point of the elliptic curve — a universal constant defined by the curve specification, known to all participants. Every public key on the curve is derived by multiplying G by a private scalar: public_key = private_key × G. In the stealth address formula, ss×G converts the shared secret scalar into a curve point so it can be added to the spend public key (also a curve point) to produce the final stealth address.


Does Radix Engine need to do this natively?

No — and this is the key point.

The derivation happens entirely off-chain, in the wallet. The Radix Engine never sees or computes the derivation. From the network’s perspective, Alice is simply sending funds to an ordinary-looking address. The RE has no idea it’s a stealth address.

What happens on-chain (RE sees this):
  → A transaction sending XRD to address_xyz
  → A metadata field containing r×G (just bytes, meaningless to RE)

What happens off-chain (wallet does this):
  → Alice's wallet: computes stealth address from Bob's meta-address
  → Bob's wallet: scans transactions, tests each r×G against scan key
  → Recognition happens locally, privately

What the off-chain infrastructure looks like

This is where the real engineering work lives. Three components are needed:

1. Wallet-side derivation library A Rust/WASM library (natural fit given Radix’s Rust stack) implementing:

  • ECDH shared secret computation

  • Stealth address derivation from meta-address + r

  • Scanning logic: given a set of transactions with hints, find which ones belong to this wallet

This library would be integrated into the Radix Wallet directly, or exposed as a standard SDK module for third-party wallets.

2. Scanning infrastructure Bob’s wallet needs to scan the network efficiently. Two options:

Option A — Full scan (trustless, heavy):
  Download all transactions containing a hint field
  Test each hint against scan_privkey locally
  Computationally expensive, but fully private

Option B — Scan delegation (lighter, trust trade-off):
  Bob shares only his scan_privkey with a trusted node/service
  The service identifies matching transactions on his behalf
  Bob keeps spend_privkey, so funds cannot be moved without him
  Privacy trade-off: the service learns which txs belong to Bob
  but cannot spend

Option B is the practical path for most users — analogous to how light wallets work today. Option A is for privacy-maximalists running their own node.

3. Hint field in transaction metadata The only protocol-level change needed: a standardised optional metadata field on transactions to carry r×G. This is not cryptographic computation by the RE — it is just a bytes field. Radix already supports transaction metadata, so this is a minor schema addition.


Honest assessment of the gap

The RE does not need native derivation support — but the ecosystem does need:

  • A standardised derivation spec (analogous to EIP-5564 for Radix) so all wallets interoperate

  • The derivation library integrated into the official Radix Wallet or SDK

  • A defined transaction metadata field for the hint

None of these require modifying the Radix Engine itself. They are wallet/SDK/standard work — which is meaningfully less complex than a protocol change, but still real engineering effort that needs to be scoped and resourced.

The closest reference implementation is the umbra.cash project on Ethereum, which built exactly this stack (off-chain derivation + scanning service + hint field) before EIP-5564 was formalised. It could serve as a practical blueprint.

Question (flightofthefox)

what derivation scheme is being used? classical cryptography is not safe from being broken later at Q-day which means eventually the links can be figured out retrospectively at some time (this is why Ethereum are deferring pushing forward with stealth addresses until they have better PQ primitives)

The quantum vulnerability is real and acknowledged

The derivation scheme described uses ECDH over a classical elliptic curve (secp256k1 or Ed25519). This is correct, and you’re right: a sufficiently powerful quantum computer running Shor’s algorithm could break elliptic curve discrete logarithm, which would mean:

Adversary with quantum capability could:
  → Recover scan_private_key from scan_public_key (published in Persona)
  → Retroactively compute all shared secrets ss = scan_privkey × (r×G)
  → Derive all historical stealth addresses
  → Link every past payment to its recipient

This is the “harvest now, decrypt later” attack vector — record all on-chain hints today, decrypt them post-Q-day. It is the same concern that is causing Ethereum to pause on EIP-5564 pending post-quantum (PQ) primitives.


What post-quantum stealth addresses would require

The core issue is that ECDH needs to be replaced with a PQ key encapsulation mechanism (KEM). The NIST-standardised candidates are:

CRYSTALS-Kyber (ML-KEM) — lattice-based KEM
  → NIST standard as of 2024
  → Designed as a drop-in replacement for ECDH in many protocols
  → Larger key sizes (~800 bytes public key vs 33 bytes for EC)

NTRU, SABER — alternatives, less momentum post-NIST selection

A PQ stealth address scheme would replace:

Classical:
  shared_secret = ECDH(r, scan_pubkey)

Post-quantum:
  (ciphertext, shared_secret) = Kyber.Encaps(scan_pubkey)
  ciphertext published on-chain as the hint
  shared_secret = Kyber.Decaps(scan_privkey, ciphertext)

The rest of the mechanism — address derivation, scanning, nullifiers — remains structurally identical. The PQ swap is localised to the KEM step.


Honest positioning of this proposal

There are two ways to frame this:

Framing A — This is a blocker If long-term unlinkability is a hard requirement, then yes, launching with classical ECDH is problematic. Any stealth payment made today could theoretically be deanonymised post-Q-day. Ethereum’s caution is reasonable.

Framing B — This is an acceptable interim trade-off Q-day is estimated to be 10-20+ years away by most credible assessments. Classical ECDH stealth addresses provide strong privacy against all present-day adversaries. The scheme can be upgraded to PQ primitives when they are production-ready in blockchain contexts — the architecture supports this substitution cleanly since the KEM is the only PQ-sensitive component.


Honest recommendation

This proposal should acknowledge the quantum vulnerability explicitly rather than ignore it, and frame the classical ECDH choice as a deliberate interim decision with a clear upgrade path:

Phase 1 (now): Classical ECDH
  → Strong privacy against current adversaries
  → Deployable with existing Radix cryptographic primitives
  → Known quantum vulnerability for long-term historical data

Phase 2 (when PQ primitives are available in Radix):
  → Swap ECDH for ML-KEM (Kyber)
  → New stealth addresses generated post-upgrade are quantum-safe
  → Historical pre-upgrade payments remain theoretically vulnerable
    (same situation as all current blockchain transactions)

This is also broadly where Ethereum will end up — they are not waiting for a fully PQ blockchain, they are waiting for PQ stealth address primitives specifically. The framing should be: we are aware, we are aligned with the industry direction, and the upgrade path is well-defined.

The stronger argument is actually that standard Radix transactions today have the same quantum vulnerability — public keys are exposed on every transaction. Stealth addresses do not make this worse in any fundamental sense; they simply add a new category of data (linkability) that becomes retrospectively attackable. Users already implicitly accept this trade-off by using any current blockchain.

Question (flightofthefox)

at the moment the link between users accounts and personas are trivially known to any dApp builders who can harvest linkages between them. (not the best design from RDXW TBH, but it is what it is). warrants perhaps being cautious about over-loading personas with this new responsibility (rather than starting with a new primitive)

The current Persona/account linkage problem

In the current Radix Wallet implementation, when a user connects to a dApp, they present both a Persona and one or more accounts. The dApp sees both, and the linkage between them is explicit and harvestable:

Current flow:
  User connects to dApp
  → dApp receives: Persona_X + [account_alice, account_bob]
  → dApp can record: Persona_X ↔ account_alice ↔ account_bob
  → This mapping is trivially known to any dApp operator
  → Aggregating across dApps → near-complete identity graph

This is not a theoretical concern — it is the current state of the system. Any dApp that logs connection data can build a detailed linkage graph over time.


Why this is specifically problematic for our proposal

If we publish stealth_meta_address inside a Persona, and that Persona is routinely linked to real accounts by dApps, then:

dApp has harvested: Persona_X ↔ account_alice
Persona_X contains: stealth_meta_address_alice
Observer can now:
  → Monitor all transactions containing hints
  → Test each hint against stealth_meta_address_alice
  → Identify all stealth payments received by Alice
  → Completely defeats the purpose of stealth addresses

The stealth_meta_address being public is only safe if the Persona itself cannot be linked to a real account. The current design breaks this assumption.


The “new primitive” suggestion is sound

Your instinct to avoid overloading Personas is architecturally correct. Instead of attaching stealth_meta_address to an existing Persona, a cleaner approach would be a dedicated Stealth Identity primitive — a new on-chain object whose sole purpose is to publish stealth receiving capability, with no connection to any Persona or account by design:

New primitive: StealthIdentity

  On-chain object containing:
    scan_public_key    : "02abc..."
    spend_public_key   : "03def..."
    version            : 1
    created_epoch      : N

  Explicitly NOT linked to:
    → Any Persona
    → Any account
    → Any transaction history
    → Any dApp connection

  Published via:
    → A dedicated registry contract
    → Or a well-known address derivation scheme
    → Or shared directly peer-to-peer (QR code, messaging...)

This object exists independently. Bob shares his StealthIdentity with Alice through whatever channel they choose — it has no on-chain connection to his real identity. Use of the persona for this task was primarily because it was already available and unlinkable to the account -granted you don’t link them inside a Dapp-, but this would be a better approach.


The trade-off this introduces

The Persona approach had one practical advantage: discoverability. If Bob’s stealth_meta_address is in his Persona, Alice can find it automatically when Bob is in her contacts. With a standalone StealthIdentity primitive, Bob needs to explicitly share it — there is no automatic discovery path.

This is not necessarily a problem. In fact it might be a feature:

Persona approach:
  → Discoverable by anyone who knows Bob's Persona
  → But Persona is linkable to account → privacy leak

StealthIdentity approach:
  → Shared deliberately, peer-to-peer
  → No automatic discovery
  → But zero linkage to real identity by construction
  → Stronger privacy guarantee

For a privacy system, deliberate sharing is arguably more appropriate than automatic discovery anyway. You share your stealth identity with people you intend to receive payments from — not with the whole world.


A hybrid path worth considering

There is a middle ground that preserves some discoverability without the linkage problem:

Option: Stealth Identity published under a stealth address itself

Bob generates a fresh stealth address S
Bob publishes his StealthIdentity at address S
Bob shares S with Alice out-of-band

Alice fetches the StealthIdentity from S
Alice derives Bob's stealth payment address
Alice sends payment

The registry entry at S has:
  → No link to Bob's Persona
  → No link to Bob's accounts
  → Just a scan_key and spend_key

This bootstraps the discovery problem using the same stealth mechanism recursively — a StealthIdentity is itself discovered via a stealth-like address, making the whole system self-consistent.


Bottom line

The critique is valid and the proposal should respond to it directly in the document. The recommendation would be:

Do not attach stealth_meta_address to Personas in the current Wallet implementation. Instead, introduce a dedicated StealthIdentity primitive that is architecturally isolated from the Persona/account system. This is a stronger design that does not depend on fixing the Persona linkage problem first — it simply sidesteps it entirely.

The Persona integration could be revisited later if and when the Radix Wallet addresses the account/Persona linkage issue at the protocol level. For now, a standalone primitive is the right call.

Question (flightofthefox)

also not sure if there’s the mechanism in place currently to make personas concrete currently (in the way that accounts become concrete when they hold an XRD balance). you probably need to check into that

On Radix, an account becomes “concrete” (i.e. exists as a real on-chain entity) when it receives its first XRD deposit. Before that, it is just a derived address — it has no on-chain presence, no state, no storage. The act of receiving funds is what instantiates it on the ledger.

The question is: does the same apply to Personas? And if so, what actually makes a Persona concrete today?


What do we know about it?

A Persona on Radix is represented by an IdentityAddress on-chain. Unlike accounts, Personas are not designed to hold funds — their purpose is to hold metadata (name, email, etc.) and serve as a signing entity for dApp connections.

The mechanism for making a Persona concrete is likely tied to the creation transaction in the Radix Wallet — when a user creates a Persona, a transaction is submitted that instantiates the identity component on-chain. But the exact mechanics of:

→ What triggers on-chain instantiation
→ Whether a Persona can exist in a "pending" state
→ What minimum on-chain footprint a Persona has
→ Whether metadata fields can be added before instantiation

…are unclear in the current Babylon implementation, and would need verification against the actual Radix Engine behaviour.


Why it matters for this proposal

If Personas require a specific instantiation mechanism, and if that mechanism is what we are proposing to extend with a stealth_meta_address field, then:

Scenario A — Personas are always concrete when created:
  → Adding a metadata field is straightforward
  → The stealth_meta_address just becomes another optional field
  → No special instantiation logic needed

Scenario B — Personas have a lazy/pending state:
  → We need to understand when metadata becomes writable
  → There may be a chicken-and-egg problem:
     the stealth_meta_address needs to exist before receiving payments,
     but the Persona may not be concrete until some other action occurs

Scenario C — The StealthIdentity primitive sidesteps this entirely:
  → As suggested in response to question 3,
     a standalone primitive would have its own well-defined
     instantiation mechanism, independent of Persona lifecycle
  → This is another argument in favour of the new primitive approach

Honest answer

This needs to be verified against current Radix documentation and ideally confirmed with the core team or a developer who has worked directly with Persona instantiation in Babylon. I would not want to commit the proposal to a specific Persona extension mechanism without confirming the on-chain lifecycle behaviour first.

The question also reinforces the conclusion from question 3: a dedicated StealthIdentity primitive, with its own clear and simple instantiation model, is architecturally cleaner than extending Personas — precisely because it avoids dependencies on the Persona lifecycle, whatever that turns out to be.

This is worth a direct conversation with the Radix core team before the proposal is formalised.

Question (flightofthefox)

unclear from proposal what the UX looks like in practice. are you “sending” to a persona/identity address and then the wallet interprets that down some new code path? requires analysis of how heavy a lift the wallet modifications would be"

What the UX would actually look like

From the sender’s perspective (Alice paying Bob), the ideal UX is nearly identical to a standard payment today:

Current UX:
  Alice opens wallet
  → Enters Bob's account address
  → Enters amount
  → Confirms

Stealth UX (target state):
  Alice opens wallet
  → Enters Bob's StealthIdentity address (or scans QR)
  → Enters amount
  → Confirms
  → Wallet silently derives ephemeral address + computes hint
  → Transaction submitted — looks like a normal payment on-chain

The stealth derivation is completely invisible to Alice. She sends to an identifier, the wallet handles everything else. This is analogous to how ENS on Ethereum resolves a human-readable name to an address under the hood — the user never sees the mechanics.

From the receiver’s perspective (Bob), the UX requires a new background process:

Bob's wallet periodically (or on demand):
  → Fetches recent transactions containing hint fields
  → Tests each hint against scan_private_key locally
  → If match found → derives the stealth address
  → Adds it to Bob's "stealth balance" aggregate
  → Displays unified balance to Bob: "Your balance: 847 XRD"

Bob sees a normal balance. He never thinks about stealth addresses. The scanning is a background job, similar to how a wallet syncs transaction history today.


The “sending to a StealthIdentity” code path

yes, this requires a new wallet code path. When Alice enters a StealthIdentity address rather than a standard account address, the wallet needs to:

Detect: "this is a StealthIdentity address, not an account address"
  → Different address prefix or type byte would signal this
  → Analogous to how Radix already distinguishes account addresses
    from component addresses from resource addresses

Then:
  1. Fetch scan_public_key and spend_public_key from the StealthIdentity
  2. Generate random ephemeral scalar r
  3. Compute shared secret: ss = hash(r×G × scan_public_key)  
  4. Derive stealth address: spend_public_key + ss×G
  5. Attach hint (r×G) to transaction metadata
  6. Submit transaction to derived stealth address as normal

Steps 1-5 are new wallet logic. Step 6 is identical to any existing payment.


How heavy a lift is the wallet modification?

Being honest, there are four distinct workstreams:

1. Cryptographic library (~medium lift) Implementing ECDH shared secret derivation and stealth address computation in the wallet’s cryptographic layer. Radix Wallet is built in Rust — the required elliptic curve operations (scalar multiplication, point addition) are available in standard Rust crypto crates like k256 or curve25519-dalek. This is well-understood cryptography, not novel research. Estimated: weeks of engineering work, not months.

2. Address type detection (~small lift) The wallet needs to recognise a StealthIdentity address and route it to the new code path. Radix already has a robust address typing system — adding a new address type is a contained change. Estimated: days.

3. Transaction metadata field (~small lift) Adding an optional hint field to the transaction structure. This touches the transaction builder but is a minor schema addition. Estimated: days, plus protocol-level agreement on the field format.

4. Scanning infrastructure (~medium-to-heavy lift) This is the most significant workstream. The wallet needs to:

  • Periodically fetch transactions containing hint fields

  • Run the scan loop against the user’s scan_private_key

  • Manage a local database of recognised stealth addresses

  • Aggregate stealth balances into a coherent UI

The scanning performance is the critical path. On a network with high transaction volume, scanning naively is expensive. Two mitigations:

  • Scan only hint-tagged transactions — if the hint field is a distinct metadata type, nodes can index and serve only those transactions, massively reducing scan overhead

  • Scan key delegation — power users can delegate scanning to a trusted service (sharing only scan_private_key, not spend_private_key), offloading the compute

Estimated: 1-3 months of focused engineering for a robust implementation.


The full picture

Workstream                    Complexity    Estimated effort
──────────────────────────────────────────────────────────
Crypto library                Medium        2-4 weeks
Address type detection        Small         2-5 days
Transaction metadata field    Small         2-5 days + protocol sign-off
Scanning infrastructure       Medium-Heavy  1-3 months
StealthIdentity primitive     Medium        2-4 weeks
──────────────────────────────────────────────────────────
Total                                       ~3-5 months focused effort

This is not trivial, but it is also not a moonshot. It is roughly comparable to implementing a new account type or a new signing scheme — the kind of work that a small dedicated team could scope and deliver in a single development cycle.


The key ask from the core team

The proposal does not require Radix to do all of this immediately. The minimum viable ask is:

1. Standardise the hint metadata field format
   → So wallets and explorers know what to look for

2. Expose elliptic curve primitives in the Radix SDK
   → So wallet developers can implement derivation without
     reimplementing curve math from scratch

3. Define the StealthIdentity address type
   → So the address space is reserved and recognised

With those three things in place, the rest of the wallet work can be built by the community — it does not need to come from the core team directly. That is the most realistic path to deployment.

1 Like