Skip to content

Commit c0e5e29

Browse files
authored
Merge branch 'develop' into docs/stellar-payment-links
2 parents a53fe39 + 877737c commit c0e5e29

9 files changed

Lines changed: 1962 additions & 2 deletions

File tree

api-reference/types.mdx

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ enum Chain {
3737
Base = "base",
3838
Stellar = "stellar",
3939
Solana = "solana",
40+
CKB = "ckb",
4041
All = "all",
4142
}
4243
```
@@ -281,6 +282,10 @@ import type {
281282
GeneratedStealthAddress,
282283
Announcement,
283284
MatchedAnnouncement,
285+
FederationRecord,
286+
FederationCache,
287+
FederationError,
288+
FederationErrorCode,
284289
} from "@wraith-protocol/sdk/chains/stellar";
285290
```
286291

@@ -330,6 +335,72 @@ interface MatchedAnnouncement extends Announcement {
330335

331336
---
332337

338+
## Stellar Federation Types
339+
340+
Exported from `@wraith-protocol/sdk/chains/stellar`:
341+
342+
```typescript
343+
import type {
344+
FederationRecord,
345+
FederationCache,
346+
FederationError,
347+
FederationErrorCode,
348+
} from "@wraith-protocol/sdk/chains/stellar";
349+
```
350+
351+
### `FederationRecord`
352+
353+
The resolved result of a `name*domain.com` lookup.
354+
355+
```typescript
356+
interface FederationRecord {
357+
federationAddress: string; // "alice*example.com" — the address that was queried
358+
accountId: string; // "GABC..." or "st:xlm:..." — resolved destination
359+
memoType?: "text" | "id" | "hash";
360+
memoValue?: string; // required for exchange deposit addresses
361+
}
362+
```
363+
364+
When `accountId` starts with `st:xlm:` it is a Wraith stealth meta-address and should be decoded with `decodeStealthMetaAddress()` before sending. Otherwise it is a plain `G...` public key.
365+
366+
### `FederationCache`
367+
368+
Pluggable cache interface accepted by `resolveStellarFederation()`. Implement this with any backend (in-memory, Redis, etc.).
369+
370+
```typescript
371+
interface FederationCache {
372+
get(key: string): Promise<FederationRecord | undefined>;
373+
set(key: string, record: FederationRecord, ttlMs: number): Promise<void>;
374+
}
375+
```
376+
377+
### `FederationErrorCode`
378+
379+
```typescript
380+
type FederationErrorCode =
381+
| "NOT_FOUND" // federation server returned 404 / unknown address
382+
| "DNS_FAILURE" // could not fetch stellar.toml (network or DNS error)
383+
| "NO_FEDERATION_SERVER" // stellar.toml exists but has no FEDERATION_SERVER field
384+
| "INVALID_TOML" // stellar.toml content is malformed
385+
| "MALFORMED_RESPONSE" // federation server response is missing required fields
386+
| "TIMEOUT" // request exceeded options.timeoutMs
387+
| "NETWORK_ERROR"; // fetch failed for any other reason
388+
```
389+
390+
### `FederationError`
391+
392+
Thrown by `resolveStellarFederation()` on any failure. Always check `err.code` rather than `err.message` for programmatic handling.
393+
394+
```typescript
395+
interface FederationError extends Error {
396+
code: FederationErrorCode;
397+
message: string;
398+
cause?: unknown; // the underlying network error or parse error, if any
399+
}
400+
```
401+
402+
---
403+
333404
## Chain Connector Types
334405

335406
Internal types used by the TEE server. Documented here for developers building custom chain connectors.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
---
2+
title: "Stellar Cryptography"
3+
description: "Design rationale, view tag derivation, and RFC-compatible cryptography for Stellar stealth payments."
4+
---
5+
6+
Wraith Protocol implements a non-interactive stealth payment scheme on Stellar. This page documents the cryptography decisions behind the implementation and exactly where each concept is realized in the SDK.
7+
8+
## Why ed25519?
9+
10+
Unlike EVM environments, which rely on `secp256k1`, the Stellar network uses the **ed25519** curve for all account addressing and signatures.
11+
To ensure that stealth accounts are valid Stellar accounts that can sign transactions, the protocol's stealth derivations must perform point addition on the ed25519 curve.
12+
13+
- **Curve definition**: `scalar.ts:1` (via [@noble/curves/ed25519](https://github.com/paulmillr/noble-curves))
14+
15+
## X25519 ECDH and Edwards-to-Montgomery Conversion
16+
17+
Standard ed25519 points (in Edwards form) are optimized for signing, not for Diffie-Hellman key exchange. To securely establish a shared secret between sender and receiver without interaction, we must use **X25519** ECDH.
18+
This requires converting the public and private ed25519 keys from Edwards coordinates to Montgomery coordinates, as specified in [RFC 7748](https://datatracker.ietf.org/doc/html/rfc7748).
19+
20+
- **Edwards-to-Montgomery conversion**: `stealth.ts:91-92`
21+
- **X25519 shared secret**: `stealth.ts:20` and `stealth.ts:93`
22+
23+
## Domain Separation Prefixes
24+
25+
We use domain-separation prefixes in SHA-256 hashes to prevent cryptographic collisions between different key derivation phases.
26+
27+
- `wraith:spending:`: Separates the derivation of the spending seed (`keys.ts:25`).
28+
- `wraith:viewing:`: Separates the derivation of the viewing seed (`keys.ts:26`).
29+
- `wraith:scalar:`: Prevents the hash scalar from colliding with the base shared secret before it's reduced modulo L (`scalar.ts:202`, `scalar.ts:220`).
30+
- `wraith:stellar:view-tag:v2:`: Domains the derivation for the 1-byte view tag (`stealth.ts:8`).
31+
- `wraith:tag:`: The legacy v1 view tag prefix (`stealth.ts:9`).
32+
33+
## View Tag Derivation
34+
35+
To avoid performing an expensive X25519 ECDH operation for every incoming transaction, the sender derives a 1-byte **view tag** and publishes it alongside their ephemeral public key.
36+
37+
**Derivation:**
38+
```
39+
view_tag = SHA-256("wraith:stellar:view-tag:v2:" || R_ephemeral || V_recipient)[0]
40+
```
41+
42+
- **Implementation**: `stealth.ts:99`
43+
- **Performance impact**: This creates a cheap public prefilter before the X25519 shared secret computation (`scan.ts:12`).
44+
- **False-positive rate**: A 1-byte tag produces a false-positive rate of `1/256` (~0.39%). For non-matching announcements, the protocol skips the expensive elliptic curve operations 99.61% of the time.
45+
46+
```mermaid
47+
sequenceDiagram
48+
participant Network
49+
participant Scanner
50+
Network->>Scanner: Fetch Announcements (R, view_tag)
51+
Note over Scanner: Compare cheap view_tag first
52+
alt Match view_tag
53+
Scanner->>Scanner: X25519(v, R) -> shared_secret
54+
Scanner->>Scanner: Derive expected stealth address
55+
alt Match Address
56+
Scanner->>Network: Recovered match!
57+
end
58+
else Mismatch view_tag
59+
Note over Scanner: Skip (99.61% of non-matches)
60+
end
61+
```
62+
63+
## Private Scalar vs. Seeds and RFC 8032
64+
65+
Standard ed25519 signing libraries expect a 32-byte seed as the private key, which they hash (via SHA-512) to produce both the private scalar and a deterministic nonce.
66+
67+
In our non-interactive stealth scheme, the stealth private key is a *derived scalar*, not a raw seed:
68+
```
69+
stealth_scalar = (spending_scalar + hash_scalar) mod L
70+
```
71+
72+
Because we only hold the resulting scalar, we cannot use off-the-shelf seed-based signing APIs. Instead, the SDK exposes a custom `signWithScalar` function to deterministically sign transactions using a raw scalar directly, while maintaining strict [RFC 8032](https://datatracker.ietf.org/doc/html/rfc8032) compatibility for ed25519 signatures.
73+
74+
- **`signWithScalar` implementation**: `scalar.ts:251`
75+
76+
## Meta-Address Encoding
77+
78+
To accept stealth payments, users publish a single "meta-address" that encapsulates both their spending and viewing public keys.
79+
80+
- **Prefix**: `st:xlm:` (`constants.ts:43`).
81+
- **Encoding**: Consists of the prefix concatenated with the hex-encoded 32-byte spending public key and the 32-byte viewing public key (`meta-address.ts:10`).
82+
- **Stellar StrKey compatibility**: To turn the final derived public stealth key into a standard Stellar address format (`G...`), we utilize Stellar's `StrKey` encoding logic (`scalar.ts:171`).
83+
84+
## Key Derivation Overview
85+
86+
```mermaid
87+
flowchart TD
88+
S(Sender) -->|Generates| r(Ephemeral Private Key 'r')
89+
r --> R(Ephemeral Public Key 'R')
90+
S --> |Recipient's| V(Viewing Public Key 'V')
91+
S --> |Recipient's| K(Spending Public Key 'K')
92+
r & V --> X25519(X25519 ECDH)
93+
X25519 --> SS(Shared Secret)
94+
R & V --> VT(View Tag)
95+
SS --> |Hash mod L| HS(Hash Scalar)
96+
HS & K --> |Point Addition| SP(Stealth Public Key)
97+
SP --> |StrKey Encoding| SA(Stellar Address 'G...')
98+
```
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# Stellar Asset Contract (SAC) Compatibility Audit
2+
**Date:** June 2026
3+
**Scope:** `stealth-sender` v1.2, `stealth-announcer` v1.1
4+
**Protocol version:** Stellar Protocol 22 (Mainnet) / Protocol 22 (Testnet)
5+
**Auditor:** Wraith Protocol internal security review
6+
7+
---
8+
9+
## Summary
10+
11+
This document records the results of a compatibility review between the Wraith `stealth-sender` and `stealth-announcer` Soroban contracts and the Stellar Asset Contract (SAC) for each asset class likely to be used in production. The goal is to identify which SAC flag combinations work transparently, which require special handling, and which are incompatible with stealth address flows.
12+
13+
---
14+
15+
## Compatibility Matrix
16+
17+
| Asset | Issuer flags | `stealth-sender` send | Trustline auto-create (`trust()`) | Clawback risk | Recommended |
18+
|---|---|---|---|---|---|
19+
| XLM (native) || ✅ Works | N/A — native asset | ❌ Cannot be clawed back | ✅ Safe |
20+
| USDC (Circle mainnet) | None | ✅ Works | ✅ Protocol 22+ (`trust()`) | ❌ No clawback | ✅ Safe |
21+
| USDC (Circle testnet) | None | ✅ Works | ✅ Protocol 22+ (`trust()`) | ❌ No clawback | ✅ Safe |
22+
| EURC (Circle mainnet) | None | ✅ Works | ✅ Protocol 22+ (`trust()`) | ❌ No clawback | ✅ Safe |
23+
| Generic asset (no flags) | None | ✅ Works | ✅ Protocol 22+ (`trust()`) | ❌ No clawback | ✅ Safe |
24+
| Asset with `AUTH_REQUIRED` | `AUTH_REQUIRED` | ⚠️ Blocked until `set_auth` | ✅ Trustline created, but blocked | ❌ No clawback | ⚠️ Manual auth step needed |
25+
| Asset with `AUTH_REVOCABLE` | `AUTH_REVOCABLE` | ✅ Works (unless deauthorized) | ✅ Works | ❌ No clawback | ⚠️ Monitor for deauth |
26+
| Asset with clawback | `AUTH_CLAWBACK_ENABLED` + `AUTH_REVOCABLE` | ✅ Works | ✅ Works |**Issuer CAN claw back** | ❌ Not recommended |
27+
| Asset with all flags | All three | ⚠️ Blocked until `set_auth` | ✅ Trustline created, but blocked |**Issuer CAN claw back** | ❌ Incompatible |
28+
| Issuer's own account (send TO issuer) || ✅ Works (burns token) | N/A | N/A | ⚠️ Burning, not transfer |
29+
| Issuer's own account (send FROM issuer) || ✅ Works (mints token) | N/A | N/A | ⚠️ Minting, not transfer |
30+
31+
---
32+
33+
## Findings
34+
35+
### F-01 — USDC and EURC: no flags, fully compatible
36+
37+
**Severity:** Informational
38+
**Assets affected:** USDC (Circle mainnet + testnet), EURC (Circle mainnet)
39+
40+
Circle issues USDC and EURC on Stellar without any restrictive flags (`AUTH_REQUIRED`, `AUTH_REVOCABLE`, `AUTH_CLAWBACK_ENABLED`). Both assets are fully compatible with stealth-sender: `transfer()` proceeds without additional authorization, and Protocol 22's `trust()` function allows stealth-sender to create the trustline on the recipient stealth address atomically within the same transaction.
41+
42+
**Action required:** None. Use USDC and EURC freely.
43+
44+
---
45+
46+
### F-02 — Protocol 22 `trust()` eliminates separate trustline setup
47+
48+
**Severity:** Informational
49+
**Assets affected:** All non-native assets
50+
51+
Prior to Protocol 22 (Yardstick), a recipient stealth address had to hold an existing trustline before any SAC `transfer()` could succeed. This required a two-transaction flow: first `changeTrust` from the stealth address private key, then the stealth-sender invocation. Since stealth address private keys are derived scalars (not standard seeds), this was cumbersome.
52+
53+
Protocol 22 introduced `SAC.trust(addr)`, callable from within a contract. `stealth-sender` v1.2 calls `token.trust(stealth_address)` before `token.transfer()` for every send. The `trust()` call is a no-op if the trustline already exists.
54+
55+
**Requirement:** The sender must include a base reserve contribution (0.5 XLM per new trustline entry) in their transaction fee budget, or fund the stealth address account to cover the reserve before sending.
56+
57+
**Action required:** Ensure sender account has at least 0.5 XLM beyond the transfer amount for each new trustline entry created.
58+
59+
---
60+
61+
### F-03 — `AUTH_REQUIRED` flag blocks transfers to new stealth addresses
62+
63+
**Severity:** High
64+
**Assets affected:** Any asset where the issuer has set `AUTH_REQUIRED_FLAG`
65+
66+
When `AUTH_REQUIRED` is set, every new trustline created by `trust()` starts in the deauthorized state. The SAC will reject `transfer()` with `BalanceDeauthorizedError` (error code 11) until the issuer explicitly calls `set_authorized(stealth_address, true)`.
67+
68+
This creates a fundamental incompatibility with stealth address flows: the sender generates a fresh one-time address per payment, but the issuer has no way to know the stealth address in advance to authorize it. Authorizing it after the fact breaks the privacy model.
69+
70+
**USDC and EURC are NOT affected** — Circle does not set `AUTH_REQUIRED` on these assets.
71+
72+
**Action required:**
73+
- Do not use `stealth-sender` with `AUTH_REQUIRED` assets.
74+
- If your use case requires `AUTH_REQUIRED` assets, use classic Stellar payment operations to the recipient's main account (not the stealth address) and handle key management separately.
75+
76+
---
77+
78+
### F-04 — `AUTH_CLAWBACK_ENABLED` allows issuer to reclaim stealth balances
79+
80+
**Severity:** High
81+
**Assets affected:** Any asset with `AUTH_CLAWBACK_ENABLED_FLAG` set (requires `AUTH_REVOCABLE_FLAG` also set)
82+
83+
When `AUTH_CLAWBACK_ENABLED` is set on the issuing account, the asset issuer can call `clawback(from, amount)` on any balance, including balances held by stealth addresses. A malicious or legally compelled issuer could claw back funds from stealth addresses without the holder's consent.
84+
85+
Additionally: when a `G...` account (stealth address) receives an asset from a contract for the first time, the clawback-enabled state is inherited from the issuing account's flags at the time the balance entry was created.
86+
87+
**USDC and EURC are NOT affected** — Circle does not set `AUTH_CLAWBACK_ENABLED`.
88+
89+
**Action required:**
90+
- Warn users before sending clawback-enabled assets via stealth payments.
91+
- The Wraith agent displays a warning when `AUTH_CLAWBACK_ENABLED` is detected.
92+
- Do not use clawback-enabled assets for stealth payments requiring unconditional custody guarantees.
93+
94+
---
95+
96+
### F-05 — `AUTH_REVOCABLE` without clawback: monitor for deauthorization
97+
98+
**Severity:** Medium
99+
**Assets affected:** Any asset with `AUTH_REVOCABLE_FLAG` set (without `AUTH_CLAWBACK_ENABLED`)
100+
101+
Assets with `AUTH_REVOCABLE` allow the issuer to call `set_authorized(address, false)`, which deauthorizes a trustline and prevents transfers. The issuer cannot, however, claw back the balance — the holder retains ownership but cannot transact.
102+
103+
Deauthorization of a stealth address trustline would trap funds: the recipient can scan and detect the payment, derive the private key, but cannot transfer the balance until the issuer re-authorizes.
104+
105+
**Action required:**
106+
- Treat `AUTH_REVOCABLE` assets as elevated-risk for stealth payment use cases.
107+
- Monitor trustline authorization status if building wallets for `AUTH_REVOCABLE` assets.
108+
109+
---
110+
111+
### F-06 — Transfer-to-issuer burns; transfer-from-issuer mints
112+
113+
**Severity:** Informational
114+
**Assets affected:** All issued assets (not native XLM)
115+
116+
SAC behaviour: sending tokens to the issuer account triggers a burn (tokens are destroyed). Sending tokens from the issuer account triggers a mint (tokens are created). The stealth-sender contract does not prevent transfers to or from the issuer address.
117+
118+
If a stealth address happens to be generated that matches the issuer address (statistically impossible with secure randomness but worth documenting), the payment would be burned rather than received.
119+
120+
**Action required:** None in practice. Document for completeness.
121+
122+
---
123+
124+
### F-07 — 64-bit vs 128-bit amount limits for account trustlines
125+
126+
**Severity:** Low
127+
**Assets affected:** All issued assets when recipient is a `G...` account
128+
129+
Trustline balances are stored as 64-bit signed integers (`i64`, max ~9.22 × 10¹⁸). The SAC interface accepts `i128`. Any `transfer()` or `trust()` call with an amount exceeding `i64::MAX` will fail with `BalanceError` (error code 10).
130+
131+
For USDC (7 decimal places), the effective maximum single stealth payment is `922,337,203,685.4775807 USDC` — far beyond any realistic payment. Not a practical concern for USDC but could matter for assets with fewer decimal places.
132+
133+
**Action required:** None for USDC. Document for asset issuers who use non-standard decimal configurations.
134+
135+
---
136+
137+
## Issuer Addresses
138+
139+
| Asset | Network | Issuer Address |
140+
|---|---|---|
141+
| USDC | Mainnet | `GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN` |
142+
| USDC | Testnet | `GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5` |
143+
| EURC | Mainnet | `GDHU6WRG4IEQXM5NZ4BMPKOXHW76MZM4Y2IEMFDVXBSDP6SJY4ITNPP` |
144+
| EURC | Testnet | `GB3Q6QDZYTHWT7E5PVS3W7FUT5GVAFC5KSZFFLPU25GO7VTC3NM2ZTVO` |
145+
| XLM | Mainnet | Native (no issuer) |
146+
| XLM | Testnet | Native (no issuer) |
147+
148+
Sources: [Circle USDC Contract Addresses](https://developers.circle.com/stablecoins/usdc-contract-addresses), verified June 2026.
149+
150+
---
151+
152+
## Test Results
153+
154+
All tests run on Stellar Testnet (Protocol 22). Transactions verified via [Stellar Expert Testnet](https://stellar.expert/explorer/testnet).
155+
156+
| Test case | Input | Expected | Result |
157+
|---|---|---|---|
158+
| USDC send via stealth-sender, no existing trustline | 100 USDC | Trust created + 100 USDC received at stealth addr | ✅ Pass |
159+
| USDC send via stealth-sender, trustline exists | 50 USDC | 50 USDC received | ✅ Pass |
160+
| USDC batch_send (3 recipients) | 3 × 100 USDC | 3 trustlines created, 3 × 100 received | ✅ Pass |
161+
| AUTH_REQUIRED asset send, unauth trustline | 100 TEST | BalanceDeauthorizedError (code 11) | ✅ Correctly blocked |
162+
| AUTH_REQUIRED asset send, pre-authed trustline | 100 TEST | 100 TEST received | ✅ Pass |
163+
| Clawback-enabled asset, post-send clawback | 100 TEST → clawback | Balance removed from stealth addr | ✅ Clawback confirmed |
164+
| USDC send, sender below 0.5 XLM reserve | 100 USDC | Fails: insufficient balance for new entry | ✅ Correctly rejected |
165+
| XLM createAccount + USDC send in 2-op tx | 1.5 XLM + 100 USDC | Account created, XLM funded, USDC trust + transfer | ✅ Pass |
166+
167+
---
168+
169+
## Conclusion
170+
171+
USDC and EURC (Circle) are fully compatible with stealth-sender and stealth-address flows on Stellar. No special handling is required beyond ensuring the sender holds 0.5 XLM per new trustline created.
172+
173+
Assets with `AUTH_REQUIRED` are incompatible with stealth payment flows — the issuer cannot pre-authorize an address that doesn't exist yet. Assets with `AUTH_CLAWBACK_ENABLED` are usable but carry issuer clawback risk that must be disclosed to users.
174+
175+
The Wraith SDK and agent surface these warnings automatically.

docs.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@
6767
"pages": [
6868
"architecture/overview",
6969
"architecture/chain-connectors",
70-
"architecture/tee"
70+
"architecture/tee",
71+
"architecture/stellar-cryptography"
7172
]
7273
},
7374
{
@@ -108,6 +109,10 @@
108109
"guides/stellar-mainnet-deployment",
109110
"guides/stellar-payment-links",
110111
"guides/stellar-payment-links",
112+
"guides/privacy-best-practices",
113+
"guides/spectre-stellar-cookbook",
114+
"guides/stellar-federation",
115+
"guides/stellar-custom-assets"
111116
]
112117
}
113118
]

0 commit comments

Comments
 (0)