From 0cf60e76ea2c523ec049188aa72f4a32033a552c Mon Sep 17 00:00:00 2001 From: Charity5654 Date: Sun, 26 Jul 2026 10:00:57 +0000 Subject: [PATCH] feat(#93): add interactive scanner playground and Stellar quickstart MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add scripts/playground/index.html — sandboxed iframe playground with dark-themed UI: meta-address + key inputs, Load demo keys shortcut, fixture announcement table, match result cards - Add scripts/playground/scanner.js — fully self-contained client-side scanner engine using only SubtleCrypto (SHA-256/SHA-512) and inline Curve25519/ed25519 point arithmetic. Zero third-party dependencies. CSP: default-src 'none'; script-src 'unsafe-inline' - Add scripts/playground/generate-fixtures.mjs — offline script that deterministically derives the demo keypair and computes the 5 fixture announcements (2 matches, 3 noise) written into scanner.js - Add guides/stellar/stellar-quickstart.mdx — 5-step quickstart guide covering key derivation, stealth address generation, scanning, spending, and EVM comparison; embeds the playground via sandboxed iframe with allow-scripts only - Update docs.json — add 'Stellar' nav group under Guides tab with the quickstart as its first page Acceptance criteria met: [x] Playground renders on the quickstart page [x] Runs entirely client-side against fixture data [x] No third-party JS (all crypto inline via Web Crypto API) --- docs.json | 6 + guides/stellar/stellar-quickstart.mdx | 165 ++++++ scripts/playground/generate-fixtures.mjs | 215 +++++++ scripts/playground/index.html | 297 ++++++++++ scripts/playground/scanner.js | 704 +++++++++++++++++++++++ 5 files changed, 1387 insertions(+) create mode 100644 guides/stellar/stellar-quickstart.mdx create mode 100644 scripts/playground/generate-fixtures.mjs create mode 100644 scripts/playground/index.html create mode 100644 scripts/playground/scanner.js diff --git a/docs.json b/docs.json index 9761769..1b51570 100644 --- a/docs.json +++ b/docs.json @@ -122,6 +122,12 @@ "guides/spectre-stellar-cookbook" ] }, + { + "group": "Stellar", + "pages": [ + "guides/stellar/stellar-quickstart" + ] + }, { "group": "Operations", "pages": [ diff --git a/guides/stellar/stellar-quickstart.mdx b/guides/stellar/stellar-quickstart.mdx new file mode 100644 index 0000000..0c236ac --- /dev/null +++ b/guides/stellar/stellar-quickstart.mdx @@ -0,0 +1,165 @@ +--- +title: "Stellar Quickstart" +description: "Derive stealth keys, scan announcements, and spend from a stealth address on Stellar — with an interactive demo" +keywords: "Stellar, stealth address, ed25519, scanner, quickstart, soroban" +--- + +This guide walks you through the complete Stellar stealth-address flow in five steps: +derive keys → generate a stealth address → send → announce → scan. +The interactive playground at the bottom lets you run the scanner against a +real batch of fixture announcements, entirely in your browser. + +## Prerequisites + +```bash +npm install @wraith-protocol/sdk @stellar/stellar-sdk +``` + +## 1. Derive Stealth Keys + +Sign the canonical Wraith message with a Stellar keypair, then pass the signature +to `deriveStealthKeys`. The two derived keys serve different purposes: +the **viewing key** detects incoming payments; the **spending key** authorizes withdrawals. + +```typescript +import { + deriveStealthKeys, + encodeStealthMetaAddress, + STEALTH_SIGNING_MESSAGE, +} from "@wraith-protocol/sdk/chains/stellar"; +import { Keypair } from "@stellar/stellar-sdk"; + +const keypair = Keypair.fromSecret("S..."); +const signature = keypair.sign(Buffer.from(STEALTH_SIGNING_MESSAGE)); +const keys = deriveStealthKeys(signature); + +// Publish this so senders can find you +const metaAddress = encodeStealthMetaAddress(keys.spendingPubKey, keys.viewingPubKey); +console.log(metaAddress); // "st:xlm:<64hex><64hex>" +``` + +## 2. Generate a Stealth Address (Sender Side) + +The sender decodes the recipient's meta-address and generates a fresh one-time +stealth address. The ephemeral public key and view tag are announced on-chain. + +```typescript +import { + decodeStealthMetaAddress, + generateStealthAddress, +} from "@wraith-protocol/sdk/chains/stellar"; + +const { spendingPubKey, viewingPubKey } = decodeStealthMetaAddress( + "st:xlm:" +); + +const { stealthAddress, ephemeralPubKey, viewTag } = generateStealthAddress( + spendingPubKey, + viewingPubKey +); + +// Send XLM to stealthAddress via Operation.createAccount +// Then call the Soroban announcer contract with: +// ephemeralPubKey (hex), viewTag (as first byte of metadata) +``` + +## 3. Scan for Incoming Payments + +`scanAnnouncements` takes the raw announcement list from `fetchAnnouncements` +(or your own indexer) and returns only the entries that belong to your keys, +together with the derived private scalar needed to spend. + +```typescript +import { + scanAnnouncements, + fetchAnnouncements, +} from "@wraith-protocol/sdk/chains/stellar"; + +// Fetch all announcements from the Soroban RPC +const announcements = await fetchAnnouncements("stellar"); + +const matched = await scanAnnouncements( + announcements, + keys.viewingKey, // 32-byte Uint8Array seed + keys.spendingPubKey, // 32-byte Uint8Array + keys.spendingScalar, // bigint +); + +for (const m of matched) { + console.log("Found payment at:", m.stealthAddress); + console.log("Private scalar: ", m.stealthPrivateScalar); +} +``` + + + View tags filter out ~255 of every 256 non-matching announcements with a + single byte comparison, making scans fast even over thousands of events. + + +## 4. Spend From a Stealth Address + +Because stealth private keys are derived scalars (not raw seeds), standard +Stellar signing does not work. Use `signStellarTransaction` instead. + +```typescript +import { + signStellarTransaction, + pubKeyToStellarAddress, +} from "@wraith-protocol/sdk/chains/stellar"; +import { Transaction, Keypair } from "@stellar/stellar-sdk"; + +// m = a matched announcement from scanAnnouncements() +const txHash = transaction.hash(); // 32-byte Buffer +const sig = signStellarTransaction(txHash, m.stealthPrivateScalar, m.stealthPubKeyBytes); + +// Attach signature and submit +const hint = Buffer.from(m.stealthPubKeyBytes.slice(0, 4)); +transaction.addSignature( + pubKeyToStellarAddress(m.stealthPubKeyBytes), + hint.toString("base64"), + sig.toString("base64"), +); +``` + +## 5. Key Differences from EVM + +| Aspect | EVM | Stellar | +|---|---|---| +| Curve | secp256k1 | ed25519 | +| ECDH | `secp256k1.getSharedSecret` | X25519 (Montgomery form) | +| Private key output | hex string | bigint scalar | +| Address format | `0x…` (20 bytes) | `G…` (56 chars) | +| Signing | secp256k1 ECDSA | `signWithScalar` / `signStellarTransaction` | + +--- + +## Interactive Scanner Demo + +Paste your own stealth meta-address and keys, or click **Load demo keys** to +scan the five pre-built fixture announcements. Everything runs client-side — +no wallet, no network calls, no third-party scripts. + +