Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/snippets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup pnpm
uses: pnpm/action-setup@v4
Expand Down Expand Up @@ -76,6 +78,15 @@ jobs:
- name: Check contract registry
run: pnpm run check:contract-registry

- name: Check redirects and anchors
env:
DOCS_DIFF_BASE: ${{ github.event.pull_request.base.sha || github.event.before }}
DOCS_DIFF_HEAD: ${{ github.event.pull_request.head.sha || github.sha }}
run: pnpm run check:redirects-and-anchors

- name: Test redirects and anchors check
run: pnpm run test:redirects-and-anchors

check-snippets-extended:
name: Compile Rust / Python / shell / config snippets
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
"light": "/assets/images/logo-black.png"
},
"favicon": "/assets/images/logo-black.png",
"redirects": [
{ "source": "/README", "destination": "/introduction" }
],
"colors": {
"primary": "#c6c6c7",
"light": "#e6e1e5",
Expand Down Expand Up @@ -102,6 +105,7 @@
"reference/error-codes",
"reference/security-disclosure",
"reference/sep-compatibility",
"reference/stellar-event-schemas",
"reference/stellar-networks",
"reference/threat-model"
]
Expand Down
29 changes: 28 additions & 1 deletion docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,31 @@ the pages that quote it. Never ship a placeholder contract id such as
Every pull request runs the snippet checker, the nav coverage check, and the
contract registry check through GitHub Actions. A separate non-blocking Stellar
testnet job is reserved for end-to-end snippet validation that depends on network
availability.
availability. The workflow also runs extended snippet validation and the
redirect and anchor checks, including their unit tests.

## Mintlify preview redirect check

After Mintlify publishes the pull request preview, run the smoke check from
this PR's checkout, supplying the actual preview base URL (for example,
`https://example.mintlify.app`). On macOS/Linux:

```bash
MINTLIFY_PREVIEW_URL=https://example.mintlify.app \
pnpm run test:preview-redirect
```

In PowerShell:

```powershell
$env:MINTLIFY_PREVIEW_URL = "https://example.mintlify.app"
pnpm run test:preview-redirect
```

The check makes a real request to `/README`, does not follow redirects, and
requires a 3xx response with a `Location` resolving to `/introduction`. The
repository's GitHub Actions workflows do not expose the Mintlify preview URL.
GitHub also requires a `workflow_dispatch` workflow to exist on the default
branch before it can be manually dispatched, so this PR uses the documented
command against the preview URL instead of adding a workflow that cannot yet
be run for this PR.
2 changes: 1 addition & 1 deletion getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,6 @@ try {
- [Bring Your Own Model](guides/bring-your-own-model) — use OpenAI or Claude instead of Gemini
- [Stellar Networks Reference](reference/stellar-networks) — passphrases, RPC endpoints, contract IDs, and reset cadence for every Stellar network
- [Stellar Fee Estimation & Budgeting](guides/stellar-fees) — learn about inclusion fees, Soroban resource fees, and fee bumps
- [Stellar React Hooks](sdk/stellar-react-hooks) — React hooks for Stellar stealth address operations
- [Stellar SDK Primitives](/sdk/chains/stellar) — key derivation, stealth addresses, scanning, and signing
- [Stellar Troubleshooting](guides/stellar-troubleshooting) — fixes for common Stellar, Soroban, and Stealth errors
- [SDK Reference](sdk/agent-client) — full API documentation
2 changes: 1 addition & 1 deletion guides/stellar-mainnet-deployment.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ stellar account fund <OPERATOR_ADDRESS> --network mainnet
| Admin | 50 XLM | Init + governance transactions |
| Operator (Spectre) | 200 XLM | Ongoing transaction fees |

Keep the operator account above **50 XLM** at all times. Set up an alert at that threshold (see [Monitoring](#monitoring--alerting)).
Keep the operator account above **50 XLM** at all times. Set up an alert at that threshold (see [Monitoring](#monitoring-and-alerting)).

### Soroban Contract Storage Fees

Expand Down
4 changes: 2 additions & 2 deletions guides/stellar-troubleshooting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ const tx = new StellarSdk.TransactionBuilder(account, { fee: "100" })

## Soroban Contract Errors

### 17. `HostError: Error(Contract, #)` / Contract Trapped
### 17. `HostError: Error(Contract, #)` / Contract Trapped {#17-hosterror-errorcontract-n-contract-trapped}
**Meaning**: The smart contract executed a `panic!` or returned a specific error code.
**Cause**: A contract assertion failed (e.g., unauthorized caller, arithmetic overflow).
**Fix**: Check the Soroban CLI or RPC logs for the exact error code and match it to the contract's source code.
Expand Down Expand Up @@ -310,4 +310,4 @@ const invocation = await myContract.myFunction({
nonce: nextNonce,
// ...
});
```
```
2 changes: 1 addition & 1 deletion guides/stellar/stellar-quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ By the end of this tutorial you will have:
**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)).

<Tip>
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.
Every stage below can be run interactively in your browser. The [Send](#7-send-a-stealth-payment) and [Withdraw](#9-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.
</Tip>

---
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"check:snippets-extended": "tsx scripts/check-snippets-extended.ts",
"check:nav-coverage": "node scripts/check-nav-coverage.mjs",
"check:contract-registry": "node scripts/check-contract-registry.mjs",
"check:redirects-and-anchors": "node scripts/check-redirects-and-anchors.mjs",
"test:redirects-and-anchors": "node --test scripts/check-redirects-and-anchors.test.mjs",
"test:preview-redirect": "node scripts/check-preview-redirect.mjs",
"check:stellar-testnet": "tsx scripts/check-stellar-testnet-snippets.ts",
"generate:stellar-reference": "tsx scripts/generate-stellar-reference.ts",
"check:stellar-reference": "tsx scripts/generate-stellar-reference.ts --check --allow-missing",
Expand All @@ -16,7 +19,7 @@
"test:playground": "playwright test --config scripts/playground/tests/playwright.config.ts",
"mint:validate": "mint validate",
"mint:broken-links": "mint broken-links",
"test": "npm run check:snippets && npm run check:nav-coverage && npm run check:contract-registry"
"test": "npm run check:snippets && npm run check:nav-coverage && npm run check:contract-registry && npm run test:redirects-and-anchors"
},
"dependencies": {
"@solana/web3.js": "^1.95.0",
Expand Down
6 changes: 3 additions & 3 deletions reference/auditor-guide.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ console.log("ephemeralPubKey", ephemeralPubKey);
[Security Disclosure Policy](/reference/security-disclosure#safe-harbor).
</Note>

## 3. Severity matrix
## 3. Severity matrix {#severity-matrix}

We use a four-level scale aligned with CVSS v3. The definitions mirror [Security Disclosure Policy → Severity Definitions](/reference/security-disclosure#severity-definitions); the right-hand column gives a concrete, Wraith-shaped example you can map a finding onto.

Expand Down Expand Up @@ -87,7 +87,7 @@ We also provide **public credit** (with your permission) in advisories and the f
[Security Disclosure Policy → Recognition and Rewards](/reference/security-disclosure#recognition-and-rewards).
</Note>

## 5. PoC repository template
## 5. PoC repository template {#poc-repository-template}

A finding is far more likely to be triaged quickly if it ships with a reproducible proof of concept. Use the template repository as a starting point:

Expand Down Expand Up @@ -127,7 +127,7 @@ From the moment your email arrives, these are our commitments. If we cannot meet

After a patch ships we coordinate the public disclosure date with you. If 90 days pass without a patch for reasons outside your control, you may disclose; we will not pursue legal or reputational action against you.

## 7. Submitting a report
## 7. Submitting a report {#submitting-a-report}

Email **security@usewraith.xyz**. Do not open a public GitHub issue or post publicly until a fix ships and coordinated disclosure is agreed. Use PGP for sensitive PoC material — the public key is at [https://usewraith.xyz/.well-known/security.txt](https://usewraith.xyz/.well-known/security.txt).

Expand Down
10 changes: 5 additions & 5 deletions reference/error-codes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Contract errors surface as `HostError: Error(Contract, #N)` in Soroban RPC respo
| `#1` | `InvalidMetaAddressLength` | The meta-address payload is not exactly 64 bytes | Passing the human-readable `st:xlm:` prefix to `register_keys` instead of the raw 64-byte key material | Decode first: `decodeStealthMetaAddress("st:xlm:...")` returns raw bytes; pass those |
| `#2` | `Unauthorized` | Caller is not the registered `registrant` | Invoking `register_keys` from a different keypair than the one in the auth envelope | Sign the transaction with the same keypair that you intend to register |

See also: [Soroban Contract Errors → `HostError`](/guides/stellar-troubleshooting#17-hostError-contract-trapped) in the troubleshooting guide.
See also: [Soroban Contract Errors → `HostError`](/guides/stellar-troubleshooting#17-hosterror-errorcontract-n-contract-trapped) in the troubleshooting guide.

---

Expand All @@ -34,7 +34,7 @@ See also: [Soroban Contract Errors → `HostError`](/guides/stellar-troubleshoot
| `#3` | `ArityMismatch` | `batch_send` vectors have different lengths | Passing `stealth_addresses`, `amounts`, `ephemeral_pub_keys`, or `metadatas` arrays of unequal length | Ensure all four arrays are the same length before building the invocation |
| `#4` | `ZeroAmount` | `amount == 0` | Passing `0` as the SAC token amount (e.g. wrong decimal scaling for USDC) | USDC uses 7 decimals: 1 USDC = `10_000_000` in the `i128`; verify your scaling |

See also: [Soroban Contract Errors → `HostError`](/guides/stellar-troubleshooting#17-hostError-contract-trapped) and [`op_no_trust`](/guides/stellar-troubleshooting#18-op_no_trust--missing-trustline).
See also: [Soroban Contract Errors → `HostError`](/guides/stellar-troubleshooting#17-hosterror-errorcontract-n-contract-trapped) and [`op_no_trust`](/guides/stellar-troubleshooting#18-op_no_trust--missing-trustline).

---

Expand All @@ -50,7 +50,7 @@ See also: [Soroban Contract Errors → `HostError`](/guides/stellar-troubleshoot
| `#6` | `Unauthorized` | Caller is not the registered owner of the name | Attempting to `update` or `release` a name from a different keypair | Sign with the same keypair used during `register` |
| `#7` | `NameAlreadyRegistered` | Calling `register` on a name that already exists | Race condition, or forgetting a previous registration | Call `resolve` first; if it returns data the name is taken |

See also: [Soroban Contract Errors → `HostError`](/guides/stellar-troubleshooting#17-hostError-contract-trapped) in the troubleshooting guide.
See also: [Soroban Contract Errors → `HostError`](/guides/stellar-troubleshooting#17-hosterror-errorcontract-n-contract-trapped) in the troubleshooting guide.

---

Expand Down Expand Up @@ -122,13 +122,13 @@ The table below maps each numbered entry in [stellar-troubleshooting.mdx](/guide
| 4 | `op_no_destination` | Stellar protocol error — see Horizon docs |
| 5 | `429 Too Many Requests` | Network / rate limiting |
| 6 | `502 / 504 Gateway` | Network / node availability |
| 7 | `retention window exceeded` | [`retention_window_exceeded`](#retention_window_exceeded) |
| 7 | `retention window exceeded` | [`retention_window_exceeded`](#soroban-rpc--indexer-errors) |
| 8 | `tx_too_late` | Stellar protocol error — see Horizon docs |
| 9 | `Freighter not installed` | Browser / wallet environment |
| 10 | `Network mismatch` | Browser / wallet environment |
| 11 | `User rejected signature` | Browser / wallet UX |
| 12 | `tx_bad_auth` / `op_bad_auth` | Stellar protocol error — see Horizon docs |
| 13 | `Derived address matches recipient` | [`Point at infinity`](#point-at-infinity) |
| 13 | `Derived address matches recipient` | [`Point at infinity`](/guides/stellar-troubleshooting#13-derived-address-matches-recipient) |
| 14 | `Zero-balance scan returning matches` | Scan logic / stale ledger state |
| 15 | `Name resolution null` | Federation / DNS availability |
| 16 | `Stealth payload too large for memo` | Stellar memo size constraint (32 bytes) |
Expand Down
101 changes: 101 additions & 0 deletions reference/stellar-event-schemas.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
---
title: "Stellar Event Schemas (v2)"
description: "Soroban event topic schemas for stealth address announcements"
---

The `stealth-announcer` contract emits events to notify indexers and clients about new stealth payments. In v2, the event topic schema has been updated to include indexed fields that allow clients to efficiently filter events before downloading the full metadata.

This reference guide documents the v2 event schema, how it differs from v1, and how to query it.

## v1 vs v2 Event Topic Comparison

### v1 Schema (Legacy)
In v1, the event emitted a single topic, requiring indexers to parse the data payload to extract routing information.
- **Topic Layout**: `("announce")`
- **Data Layout**: `(caller, scheme_id, stealth_address, ephemeral_pub_key, metadata)`

### v2 Schema (Current)
In v2, key routing fields have been moved to the event topics to enable native filtering via the Soroban RPC `getEvents` method.
- **Topic Layout**: `("announce", scheme_id, view_tag_bucket, metadata_kind)`
- **Data Layout**: `(caller, stealth_address, ephemeral_pub_key, metadata)`

## v2 Topic Layout Details

The v2 event emits exactly four topics:

1. **`"announce"`**: The literal string identifier for the event.
2. **`scheme_id`** (u32): The stealth address scheme being used (e.g., `1` for the standard ed25519 scheme).
3. **`view_tag_bucket`** (u32): A deterministic bucket derived from the view tag to allow prefix filtering.
4. **`metadata_kind`** (u32): The type of metadata attached to the event.

### `view_tag_bucket` Derivation Rule

To reduce false positives when scanning announcements, clients can filter by the `view_tag_bucket`.
- **Rule**: The bucket is derived directly from the first byte of the metadata (`metadata[0]`).
- **Stability**: This derivation is stable and guaranteed not to change for a given `metadata_kind`.

When querying the RPC, indexers can specify their expected `view_tag_bucket` to dramatically reduce the number of events they need to fetch and process.

### `metadata_kind` Values & Forward-Compat

The `metadata_kind` field ensures forward compatibility for future upgrades to the announcement payload.

- **`0`**: Standard stealth payment metadata (view tag included).
- **`1+`**: Reserved for future use (e.g., encrypted amounts, multi-asset routing).

**Forward-Compat Semantics**: Indexers and clients *must* gracefully ignore events with a `metadata_kind` they do not recognize. This allows new metadata formats to be deployed without breaking existing indexers.

## Example `getEvents` Filter Queries

You can use the Soroban RPC `getEvents` endpoint to filter for specific topics.

### 1. Fetch all v2 announcements for Scheme 1
```json
{
"startLedger": 123456,
"filters": [
{
"type": "contract",
"contractIds": ["<v2-announcer-contract-id>"],
"topics": [
["announce"],
["1"],
["*"],
["*"]
]
}
],
"pagination": { "limit": 100 }
}
```

### 2. Filter by `view_tag_bucket` (e.g., Bucket 42)
This is the recommended query for clients looking for their own transactions.

```json
{
"startLedger": 123456,
"filters": [
{
"type": "contract",
"contractIds": ["<v2-announcer-contract-id>"],
"topics": [
["announce"],
["1"],
["42"],
["0"]
]
}
],
"pagination": { "limit": 100 }
}
```

## Migration & Indexer Recommendations

The transition from v1 to v2 involves a new deployment of the `stealth-announcer` contract.

- **Migration Timing**: v1 events remain readable and will not be deleted. v2 is a strictly new deployment with a new contract ID.
- **Indexer Recommendations**: During the transition period, indexers and wallets *must* listen to both the v1 and v2 contract IDs to ensure no announcements are missed.

You can query both simultaneously by including both contract IDs in your `getEvents` filter, or by executing parallel queries for the different topic structures.
41 changes: 41 additions & 0 deletions scripts/check-preview-redirect.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const previewUrl = process.env.MINTLIFY_PREVIEW_URL;

if (!previewUrl) {
console.error("Set MINTLIFY_PREVIEW_URL to the base URL of the Mintlify PR preview.");
process.exit(1);
}

let baseUrl;
try {
baseUrl = new URL(previewUrl);
} catch {
console.error(`MINTLIFY_PREVIEW_URL is not a valid URL: ${previewUrl}`);
process.exit(1);
}

if (!/^https?:$/.test(baseUrl.protocol)) {
console.error("MINTLIFY_PREVIEW_URL must use HTTP or HTTPS.");
process.exit(1);
}

const requestUrl = new URL("/README", baseUrl);
const response = await fetch(requestUrl, { redirect: "manual" });

if (response.status < 300 || response.status >= 400) {
console.error(`Expected ${requestUrl} to return a 3xx redirect; received ${response.status}.`);
process.exit(1);
}

const location = response.headers.get("location");
if (!location) {
console.error(`Expected ${requestUrl} to include a Location header; received none.`);
process.exit(1);
}

const destination = new URL(location, requestUrl);
if (destination.pathname !== "/introduction") {
console.error(`Expected ${requestUrl} to resolve to /introduction; Location was ${location}.`);
process.exit(1);
}

console.log(`Verified ${requestUrl} returns ${response.status} redirect to ${destination.pathname}.`);
Loading
Loading