Skip to main content
Low-level stealth address functions for Stellar using ed25519. Import from @wraith-protocol/sdk/chains/stellar. Most developers should use the Agent Client instead. These primitives are for power users building custom stealth address integrations on Stellar.

Installation

Import

Types

Key Differences from EVM

Constants


Functions

deriveStealthKeys(signature)

Derive spending and viewing key pairs from a 64-byte ed25519 signature.
Algorithm:
  1. spendingKey = SHA-256("wraith:spending:" || signature) — 32-byte seed
  2. viewingKey = SHA-256("wraith:viewing:" || signature) — 32-byte seed
  3. Each seed is expanded via seedToScalar() (SHA-512 + clamping)
  4. Public keys derived via ed25519.getPublicKey(seed)
Domain-separated hashing is used instead of the EVM approach of splitting r/s components, because ed25519 signature components don’t have the same independence.

seedToScalar(seed)

Convert a 32-byte ed25519 seed to its clamped scalar. Mirrors standard ed25519 private key expansion.
Algorithm:
  1. h = SHA-512(seed) — 64 bytes
  2. a = h[0:32] — lower half
  3. Clamp: a[0] &= 248; a[31] &= 127; a[31] |= 64
  4. Interpret as little-endian bigint

computeSharedSecret(privateKey, publicKey)

Compute an ECDH shared secret using X25519 (Montgomery form conversion).
ed25519 keys are converted to X25519 (Montgomery form) before performing Diffie-Hellman:
  1. privX = edwardsToMontgomeryPriv(privateKey)
  2. pubX = edwardsToMontgomeryPub(publicKey)
  3. shared = x25519.getSharedSecret(privX, pubX)

computeViewTag(sharedSecret)

Compute the view tag from a shared secret.
SHA-256("wraith:tag:" || sharedSecret)[0]

hashToScalar(sharedSecret)

Hash a shared secret to a scalar value for stealth address derivation.
SHA-256("wraith:scalar:" || sharedSecret) interpreted as little-endian bigint, reduced mod L.

generateStealthAddress(spendingPubKey, viewingPubKey, ephemeralSeed?)

Generate a one-time stealth address for a Stellar recipient.
Algorithm:
  1. Generate random ephemeral ed25519 seed
  2. Compute shared secret via X25519 ECDH
  3. viewTag = computeViewTag(sharedSecret)
  4. hScalar = hashToScalar(sharedSecret)
  5. stealthPoint = spendingPubKey + hScalar * G (ed25519 point addition)
  6. Encode as Stellar G... address via StrKey.encodeEd25519PublicKey

checkStealthAddress(ephemeralPubKey, viewingKey, spendingPubKey, viewTag)

Check if an announcement belongs to you.

scanAnnouncements(announcements, viewingKey, spendingPubKey, spendingScalar)

Scan announcements and return matches with their private scalars.
The fourth argument is spendingScalar (bigint), not spendingKey like in the EVM module.

deriveStealthPrivateScalar(spendingScalar, viewingKey, ephemeralPubKey)

Derive the private scalar for a specific stealth address.

signWithScalar(message, scalar, publicKey)

Sign a message using a raw scalar instead of a seed. Required because stealth private keys are derived scalars that can’t be used with Keypair.fromRawEd25519Seed().
The stealth scalar (spendingScalar + hashScalar) % L is not necessarily clamped, so standard Stellar signing functions don’t work.

signStellarTransaction(txHash, stealthScalar, stealthPubKey)

Sign a Stellar transaction hash with a stealth private scalar.

encodeStealthMetaAddress(spendingPubKey, viewingPubKey)

Encode two 32-byte public keys into a Stellar stealth meta-address.

decodeStealthMetaAddress(metaAddress)

Decode a Stellar meta-address back into its component keys.

deriveStealthPubKey(spendingPubKey, hashScalar)

Derive a stealth public key from a spending public key and hash scalar.

pubKeyToStellarAddress(publicKey)

Convert a 32-byte ed25519 public key to a Stellar G... address.

bytesToHex(bytes) / hexToBytes(hex)

Utility functions for converting between Uint8Array and hex strings.

End-to-End Flow

Stellar-Specific Considerations

  • Account creation: Stellar requires accounts to exist with a minimum balance (1 XLM). Sending to a new stealth address uses Operation.createAccount, not Operation.payment.
  • Announcements: Come from Soroban contract events via sorobanServer.getEvents(), not a subgraph. Use fetchAnnouncements("stellar") to handle this automatically.
  • Signing: Must use signWithScalar() / signStellarTransaction() because stealth scalars aren’t valid seeds for Keypair.fromRawEd25519Seed().

Chain Deployments

The SDK ships with deployed contract addresses and RPC URLs for supported Stellar networks.

getDeployment(chain)

Supported Stellar Networks

Fetching Announcements

fetchAnnouncements(chain?, sorobanUrl?)

Fetches all stealth address announcements from the Soroban RPC for the specified network. Handles ledger range detection and pagination automatically.
This replaces the need to manually query sorobanServer.getEvents() and parse XDR-encoded event data.