§ Developers · /developers/stealth-addresses
Stealth addresses.
A stealth address is a one-time recipient derived from a long-lived viewing key. Observers cannot link two payments to the same recipient. Adamant uses ML-KEM-768 for the underlying key-agreement, providing post-quantum security at the privacy layer (WP §7.2).
§ 01 / Derivation
Sender-side.
// Recipient publishes (V, T)
// V = ML-KEM-768 public encapsulation key (view)
// T = Ed25519 + ML-DSA hybrid public key (transfer)
// Sender:
let (ct, k) = mlkem768_encapsulate(V);
let nonce = SHA3-256("ADAMANT-v1-stealth-nonce" || ct);
let one_time = T + hash_to_scalar(k || nonce) * G; // Ed25519 component
+ ML-DSA derivation // PQ component
let dest_addr = bech32m("adm1", SHA3-256(one_time));
// Tx envelope carries (ct, dest_addr, ciphertext_memo, halo2_proof). § 02 / Discovery
Recipient-side.
// Recipient scans the chain for envelopes whose ct decapsulates to a key
// matching their view key. Match probability per scan is small but
// constant; the wallet performs this in a background scan.
for env in chain.envelopes() {
let k = mlkem768_decapsulate(view_sk, env.ct);
if hash_to_scalar(k || nonce(env.ct)) yields env.dest_addr {
// payment is to us; decrypt memo and amount
}
} § 03 / URI scheme
Payment requests.
adamant:<addr>
?amount=<base_units> // optional
&memo=<urlencoded> // optional
&label=<urlencoded> // optional, sender-side hint only
&expiry=<unix-ms> // optional, request invalid after
// Wallets parse, present amount in ADM (10^-9 base units),
// encrypt memo with the recipient's view key,
// and broadcast as a shielded transaction.