Skip to content

Commit d0d48db

Browse files
authored
feat: add guided send flow to the interactive playground (#147)
Extends the scan-only playground into a four-step derive, send, scan, and withdraw flow with fixture Horizon events, embeds each step in the relevant docs pages, and adds Playwright smoke tests that enforce the zero-external-requests gate. Fixes #134
1 parent 411c537 commit d0d48db

15 files changed

Lines changed: 1770 additions & 462 deletions

.github/workflows/snippets.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,33 @@ jobs:
5959

6060
- name: Validate Stellar testnet snippets
6161
run: pnpm run check:stellar-testnet
62+
63+
playground-smoke:
64+
name: Playground smoke test
65+
runs-on: ubuntu-latest
66+
steps:
67+
- name: Checkout
68+
uses: actions/checkout@v4
69+
70+
- name: Setup pnpm
71+
uses: pnpm/action-setup@v4
72+
with:
73+
version: 10
74+
75+
- name: Setup Node
76+
uses: actions/setup-node@v4
77+
with:
78+
node-version: 22
79+
cache: pnpm
80+
81+
- name: Install dependencies
82+
run: pnpm install --frozen-lockfile
83+
84+
- name: Verify playground fixtures are in sync
85+
run: pnpm run check:playground-fixtures
86+
87+
- name: Install Playwright browser
88+
run: pnpm exec playwright install --with-deps chromium
89+
90+
- name: Run playground smoke tests
91+
run: pnpm run test:playground

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
node_modules/
2-
.npm/
2+
.npm/
3+
test-results/
4+
playwright-report/

api-reference/fetch-announcements-stream.mdx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,25 @@ for await (const announcement of stream) {
434434
}
435435
```
436436

437+
---
438+
439+
## Try It: Scan Announcements in Your Browser
440+
441+
The playground below parses a batch of Horizon-shaped fixture events — the same `topic`/`value` shape `fetchAnnouncementsStream` yields on Stellar — and scans them client-side with the view-tag fast filter and Ed25519 point math. No wallet, no network calls.
442+
443+
<iframe
444+
src="/scripts/playground/index.html?step=scan"
445+
sandbox="allow-scripts allow-same-origin"
446+
width="100%"
447+
height="760"
448+
style={{ border: "1px solid #2a2f3d", borderRadius: "8px" }}
449+
title="Wraith stealth playground — scan step"
450+
/>
451+
452+
<Note>
453+
Two of the five fixture announcements match the demo keys (pre-filled via **Load demo keys**); the other three are noise with the wrong view tag. You can also construct your own announcement in the [Send step](/guides/stellar-quickstart) and scan it here.
454+
</Note>
455+
437456
### Resilient Stream with Retention Fallback
438457

439458
```typescript

api-reference/stealth-keys.mdx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,25 @@ const keys = await deriveStealthKeysFromPasskey({
8686
});
8787
```
8888

89-
For a complete walkthrough on setting up passkey authentication with stealth transactions, refer to our [Passkey Signing Guide](../guides/stellar/passkey-signing.mdx).
89+
For a complete walkthrough on setting up passkey authentication with stealth transactions, refer to our [Passkey Signing Guide](../guides/stellar/passkey-signing.mdx).
90+
91+
---
92+
93+
## Try It: Derive Keys in Your Browser
94+
95+
The playground below runs the same derivation the SDK performs, entirely client-side against canned fixtures — no wallet, no network calls. Paste any 64-byte signature (the demo one is pre-filled), and watch the two seeds split into a scan key, a spend key, and your stealth meta-address.
96+
97+
<iframe
98+
src="/scripts/playground/index.html?step=derive"
99+
sandbox="allow-scripts allow-same-origin"
100+
width="100%"
101+
height="660"
102+
style={{ border: "1px solid #2a2f3d", borderRadius: "8px" }}
103+
title="Wraith stealth playground — derive step"
104+
/>
105+
106+
<Note>
107+
The demo signature is deterministic and the derived keys are not connected to any real wallet. The meta-address it produces matches the fixtures used by the [scan step](/api-reference/fetch-announcements-stream).
108+
</Note>
109+
110+
Continue through the guided flow by opening the same playground on the [Send](/guides/stellar-quickstart), [Scan](/api-reference/fetch-announcements-stream), and [Withdraw](/guides/stellar-quickstart) steps.

guides/stellar/stellar-quickstart.mdx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ By the end of this tutorial you will have:
1414

1515
**Prerequisites:** Node.js 18+, a Wraith API key ([sign up at usewraith.xyz](https://usewraith.xyz)), and the Freighter browser extension installed ([get it here](https://www.freighter.app)).
1616

17+
<Tip>
18+
Every stage below can be run interactively in your browser. The [Send](#send-a-stealth-payment) and [Withdraw](#withdraw-to-your-wallet) sections embed a client-side playground that runs the exact same flow against canned fixtures — no wallet or network calls needed. Continue the full guided flow on the [derive](/api-reference/stealth-keys) and [scan](/api-reference/fetch-announcements-stream) pages.
19+
</Tip>
20+
1721
---
1822

1923
## 1. Install Dependencies
@@ -328,6 +332,23 @@ Alice's agent scans for announcements, matches them using her viewing key, and d
328332
A view tag is the first byte of `SHA-256("wraith:tag:" || sharedSecret)`. It allows scanners to reject ~255/256 non-matching announcements with a single byte comparison — no expensive Ed25519 point operations needed. This makes scanning fast even over thousands of announcements.
329333
</Accordion>
330334

335+
### Try It: Construct an Announcement
336+
337+
Construct a mock stealth payment in your browser — the playground generates the one-time ephemeral keypair, derives the stealth address, and builds the Horizon announcement event the `stealth-announcer` contract would emit. The announcement lands in the scan batch, so it shows up in the [scan step](/api-reference/fetch-announcements-stream).
338+
339+
<iframe
340+
src="/scripts/playground/index.html?step=send"
341+
sandbox="allow-scripts allow-same-origin"
342+
width="100%"
343+
height="640"
344+
style={{ border: "1px solid #2a2f3d", borderRadius: "8px" }}
345+
title="Wraith stealth playground — send step"
346+
/>
347+
348+
<Note>
349+
The demo recipient meta-address is pre-filled and deterministic; the payment it produces is a simulation, not an on-chain transaction.
350+
</Note>
351+
331352
---
332353

333354
## 8. Scan for Incoming Payments
@@ -395,6 +416,23 @@ console.log(privacy.response);
395416
To protect your privacy, space withdrawals at least **1 hour apart** and avoid withdrawing uniform round amounts. The agent's privacy scoring algorithm checks for timing patterns, address correlation, and dust amounts. See [Privacy Best Practices](/guides/privacy-best-practices) for details.
396417
</Info>
397418

419+
### Try It: Withdraw a Stealth Balance
420+
421+
The playground below derives the withdrawable balances from the fixture announcements with the demo keys, then shows the simulated withdrawal transaction each one would sign with its stealth private scalar.
422+
423+
<iframe
424+
src="/scripts/playground/index.html?step=withdraw"
425+
sandbox="allow-scripts allow-same-origin"
426+
width="100%"
427+
height="640"
428+
style={{ border: "1px solid #2a2f3d", borderRadius: "8px" }}
429+
title="Wraith stealth playground — withdraw step"
430+
/>
431+
432+
<Note>
433+
This is a simulation — the destination wallet is a deterministic demo address. The private scalar shown is what your own agent would use to sign a real withdrawal.
434+
</Note>
435+
398436
### Withdrawing to Any Destination
399437

400438
```typescript

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
"check:stellar-testnet": "tsx scripts/check-stellar-testnet-snippets.ts",
99
"generate:stellar-reference": "tsx scripts/generate-stellar-reference.ts",
1010
"check:stellar-reference": "tsx scripts/generate-stellar-reference.ts --check --allow-missing",
11+
"generate:playground-fixtures": "node scripts/playground/generate-fixtures.mjs",
12+
"check:playground-fixtures": "node scripts/playground/generate-fixtures.mjs --check",
13+
"test:playground": "playwright test --config scripts/playground/tests/playwright.config.ts",
1114
"mint:validate": "mint validate",
1215
"mint:broken-links": "mint broken-links",
1316
"test": "npm run check:snippets && npm run check:nav-coverage"
@@ -18,6 +21,7 @@
1821
"@wraith-protocol/sdk": "^1.4.5"
1922
},
2023
"devDependencies": {
24+
"@playwright/test": "^1.49.1",
2125
"@types/node": "^22.10.2",
2226
"tsx": "^4.19.2",
2327
"typescript": "^5.7.2"

pnpm-lock.yaml

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/playground/fixtures.json

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
{
2+
"recipient": {
3+
"signature": "98822231bc29a7b7fa05b22cb955271eb091952cdc574040833e8710c21a57febc8a29ac3a269ecf90a8000ba429a629f7a03475bcf0727f3862099bbc3b97bc",
4+
"spendingKey": "8abc3ca12f1ea7d8a035035b30495dab4580312cda2ddc408bfcb0f05a4aeadf",
5+
"viewingKey": "0d474ed106c31d9be5e40d23987e05e7e7f250f618abee45e78ce8a5a89d5c16",
6+
"spendingScalar": "44210394736376739873962455346134452853705630680662457980493046177521038273944",
7+
"viewingScalar": "48307637337068610111822068508286914851033825534515029969832724895260692015208",
8+
"spendingPubKey": "eb8452e938d04e9a56ef69c47dacd8224464b030b5ca569d5b4e4399f8d0fb55",
9+
"viewingPubKey": "29a8dd877a3803289ab3a62ac39cce4a99021a3cd0fac6ad982e051c8fa769dc",
10+
"metaAddress": "st:xlm:eb8452e938d04e9a56ef69c47dacd8224464b030b5ca569d5b4e4399f8d0fb5529a8dd877a3803289ab3a62ac39cce4a99021a3cd0fac6ad982e051c8fa769dc",
11+
"walletAddress": "GB4X7TDIRWAXKYRAYRXSTY27DZUTQKEMJKV7GBKZ3RVJJS5XCHAELUZI"
12+
},
13+
"announcerContractId": "CCJLJ2QRBJAAKIG6ELNQVXLLWMKKWVN5O2FKWUETHZGMPAD4MHK7WVWL",
14+
"events": [
15+
{
16+
"type": "contract",
17+
"contract_id": "CCJLJ2QRBJAAKIG6ELNQVXLLWMKKWVN5O2FKWUETHZGMPAD4MHK7WVWL",
18+
"ledger": 51234000,
19+
"ledger_close_time": "2026-08-20T12:00:00.000Z",
20+
"id": "0000000000000000030dc4d00000000000000000",
21+
"paging_token": "51234000-0",
22+
"topic": [
23+
"AAAADwAAAAhhbm5vdW5jZQ==",
24+
"AAAAAwAAAAE=",
25+
"AAAAEgAAAAAAAAAAIqtRXPL2GmdCP+bde4hGNCoXiH+wKZAxd3/TheC3t/0="
26+
],
27+
"value": "AAAAEAAAAAEAAAADAAAAEgAAAAAAAAAAFjM5gVlCLWe12ocr2Z56+SK0zkRi5WgaOs8iFHUi6d4AAAANAAAAIBYzOYFZQi1ntdqHK9meevkitM5EYuVoGjrPIhR1IuneAAAADQAAACBfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
28+
"_yours": true
29+
},
30+
{
31+
"type": "contract",
32+
"contract_id": "CCJLJ2QRBJAAKIG6ELNQVXLLWMKKWVN5O2FKWUETHZGMPAD4MHK7WVWL",
33+
"ledger": 51234001,
34+
"ledger_close_time": "2026-08-21T12:00:00.000Z",
35+
"id": "0000000000000000030dc4d10000000000000000",
36+
"paging_token": "51234001-1",
37+
"topic": [
38+
"AAAADwAAAAhhbm5vdW5jZQ==",
39+
"AAAAAwAAAAE=",
40+
"AAAAEgAAAAAAAAAAbwoxfo1UleU30uxYWjsBtQpSXMVYCGBkney2/oqlwWU="
41+
],
42+
"value": "AAAAEAAAAAEAAAADAAAAEgAAAAAAAAAAYaeYyrc6YoZo7/DMSlz1HVaHyUe/xnQQAIBTgEnhNjoAAAANAAAAIGGnmMq3OmKGaO/wzEpc9R1Wh8lHv8Z0EACAU4BJ4TY6AAAADQAAACCFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
43+
"_yours": false
44+
},
45+
{
46+
"type": "contract",
47+
"contract_id": "CCJLJ2QRBJAAKIG6ELNQVXLLWMKKWVN5O2FKWUETHZGMPAD4MHK7WVWL",
48+
"ledger": 51234002,
49+
"ledger_close_time": "2026-08-22T12:00:00.000Z",
50+
"id": "0000000000000000030dc4d20000000000000000",
51+
"paging_token": "51234002-2",
52+
"topic": [
53+
"AAAADwAAAAhhbm5vdW5jZQ==",
54+
"AAAAAwAAAAE=",
55+
"AAAAEgAAAAAAAAAAfLtYHQxZk09xS3vObXcVqtYviB7UFT3OGORCXiSOlGA="
56+
],
57+
"value": "AAAAEAAAAAEAAAADAAAAEgAAAAAAAAAAUhyk20jvoleCDbryfv10BQMOIOreUmtoALmC+gpGy/AAAAANAAAAIFIcpNtI76JXgg268n79dAUDDiDq3lJraAC5gvoKRsvwAAAADQAAACBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
58+
"_yours": true
59+
},
60+
{
61+
"type": "contract",
62+
"contract_id": "CCJLJ2QRBJAAKIG6ELNQVXLLWMKKWVN5O2FKWUETHZGMPAD4MHK7WVWL",
63+
"ledger": 51234003,
64+
"ledger_close_time": "2026-08-23T12:00:00.000Z",
65+
"id": "0000000000000000030dc4d30000000000000000",
66+
"paging_token": "51234003-3",
67+
"topic": [
68+
"AAAADwAAAAhhbm5vdW5jZQ==",
69+
"AAAAAwAAAAE=",
70+
"AAAAEgAAAAAAAAAATam9D4M2xhWIQ6h03y9kOlcHQ2aFMOJ8w42VdMJRWgM="
71+
],
72+
"value": "AAAAEAAAAAEAAAADAAAAEgAAAAAAAAAA3FLnwEKlqKFW90zpyt/IdNo0SGTTkrCClOXAzD45dYkAAAANAAAAINxS58BCpaihVvdM6crfyHTaNEhk05KwgpTlwMw+OXWJAAAADQAAACCVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
73+
"_yours": false
74+
},
75+
{
76+
"type": "contract",
77+
"contract_id": "CCJLJ2QRBJAAKIG6ELNQVXLLWMKKWVN5O2FKWUETHZGMPAD4MHK7WVWL",
78+
"ledger": 51234004,
79+
"ledger_close_time": "2026-08-24T12:00:00.000Z",
80+
"id": "0000000000000000030dc4d40000000000000000",
81+
"paging_token": "51234004-4",
82+
"topic": [
83+
"AAAADwAAAAhhbm5vdW5jZQ==",
84+
"AAAAAwAAAAE=",
85+
"AAAAEgAAAAAAAAAAjsBTk06PWenk4B0q3B5BtX+fPfEsJXehKsCI42TzQ30="
86+
],
87+
"value": "AAAAEAAAAAEAAAADAAAAEgAAAAAAAAAAsbT/rDG0eoPrT66DUFMJ4AqhkAUfk08F/td0yuN1INwAAAANAAAAILG0/6wxtHqD60+ug1BTCeAKoZAFH5NPBf7XdMrjdSDcAAAADQAAACD6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
88+
"_yours": false
89+
}
90+
]
91+
}

0 commit comments

Comments
 (0)