Skip to content

feat: add stealth address#18

Open
snawaz wants to merge 5 commits into
mainfrom
snawaz/stealth
Open

feat: add stealth address#18
snawaz wants to merge 5 commits into
mainfrom
snawaz/stealth

Conversation

@snawaz

@snawaz snawaz commented May 20, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added Handle tab to create and manage stealth pools with custom handles
    • Enabled payments directly to stealth handle recipients with automatic private routing
  • Documentation

    • Updated README with ephemeral RPC configuration and stealth pool behavior documentation

@vercel

vercel Bot commented May 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pay Ready Ready Preview, Comment Jun 9, 2026 6:45am

Request Review

snawaz commented May 20, 2026

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@snawaz snawaz requested a review from GabrielePicco May 29, 2026 20:28
@snawaz snawaz marked this pull request as ready for review May 29, 2026 20:28

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
components/one/payment-card.tsx (1)

1339-1361: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Stale dependency: isValidReceiver is in deps but not used in callback body.

The guard at lines 1251-1252 checks isStealthReceiver and resolvedReceiver directly rather than using isValidReceiver. Since isValidReceiver is derived from those values, this is functionally correct but the dependency is misleading. Consider either:

  1. Remove isValidReceiver from deps (it's derived from values already in deps)
  2. Use isValidReceiver in the guard: if (!isValidReceiver) return;

Option 2 is cleaner and more explicit:

Suggested fix
   const handleSend = useCallback(async () => {
     if (!publicKey || !signTransaction || !connected) return;
-    if (isResolvingRecipient) return;
-    if (!isStealthReceiver && !resolvedReceiver) return;
+    if (isResolvingRecipient || !isValidReceiver) return;
     if (!rawAmount || rawAmount === "0") return;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/one/payment-card.tsx` around lines 1339 - 1361, The dependency
list includes isValidReceiver but the effect body checks isStealthReceiver and
resolvedReceiver directly; update the effect callback so it uses isValidReceiver
in the guard (e.g., replace the current checks with if (!isValidReceiver)
return;) and keep isValidReceiver in the dependency array, ensuring the effect's
logic references the derived value rather than the underlying pieces (symbols to
change: isValidReceiver, isStealthReceiver, resolvedReceiver inside the
useEffect callback and the dependency array near the closing bracket).
components/one/trade-hub.tsx (1)

179-230: 🧹 Nitpick | 🔵 Trivial

Align h query param usage with HandleCard behavior
trade-hub.tsx treats the presence of the h query param as a signal to activate the handle tab (hasHandleSelection = Boolean(searchParams.get("h"))), but HandleCard never reads searchParams.get("h") and instead initializes handle from getStoredStealthHandle(owner).
If h is meant for deep-linking a specific handle, parse searchParams.get("h") in HandleCard and use it to pre-populate the input; otherwise remove/rename this “unused” query-param detection to avoid implying the value is consumed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/one/trade-hub.tsx` around lines 179 - 230, trade-hub.tsx currently
treats the presence of the "h" query param (hasHandleSelection =
Boolean(searchParams.get("h"))) as selecting the handle tab but HandleCard
ignores that param and instead uses getStoredStealthHandle(owner); fix by wiring
the "h" deep-link into HandleCard: read searchParams.get("h") (or accept it as a
prop from trade-hub) and, in HandleCard's initialization logic (where it
currently calls getStoredStealthHandle(owner)), prefer the parsed "h" value to
pre-populate the input/selection (falling back to getStoredStealthHandle(owner)
when "h" is absent); alternatively, if "h" is not intended as a deep-link,
remove/rename the Boolean(searchParams.get("h")) check in trade-hub
(updateTabUrl/hasHandleSelection) to stop implying consumption — reference
symbols: hasHandleSelection, searchParams.get("h"), HandleCard,
getStoredStealthHandle(owner), and updateTabUrl.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/api/payments/send/route.ts`:
- Around line 78-89: Wrap the call to request.json() in a try/catch and return
an HTTP 400 response for malformed JSON instead of letting it bubble to the
generic 502; specifically, around the code that assigns body and destructures
signedTransaction, blockhash, lastValidBlockHeight, sendTo (and the similar
block later around the other request.json() usage), catch a SyntaxError (or any
parse error) and return a Response with status 400 and a short error message
indicating invalid JSON payload so clients receive a proper client error.

In `@app/api/payments/stealth-pool/route.ts`:
- Around line 76-82: The auth header check using authHeader?.startsWith("Bearer
") is case-sensitive and should accept any casing of the scheme; update the
logic in route.ts where authHeader is read (variable authHeader) and the
startsWith check to perform a case-insensitive match (e.g., test the scheme
portion with toLowerCase() === "bearer" or use a case-insensitive regex like
/^bearer\s+/i), then extract the token after the scheme safely (split on
whitespace and use the second element) and return the same 401 response if the
scheme is not "bearer" or the token is missing.

In `@lib/payment-transactions.ts`:
- Around line 138-147: The POST fetches to "/api/payments/send" (the call that
sends serializeSignedPaymentTransaction(signedTransaction) and
unsignedTransaction fields) need a client-side timeout to avoid hanging; wrap
each fetch in an AbortController with a setTimeout that calls controller.abort()
after a configurable timeout (e.g. 10s), pass controller.signal to fetch, and
clear the timeout on success; also handle the abort by catching the thrown
DOMException/AbortError and surface a clear error (or return a timeout-specific
error) so callers of this payment flow can recover.

In `@lib/stealth-handles.ts`:
- Around line 25-27: The setter setStoredStealthHandle currently writes directly
to localStorage and can throw in restricted environments; update it to mirror
the getter’s safety guards by checking for a window/localStorage environment
(e.g., typeof window !== 'undefined' and window.localStorage) and wrapping the
write in a try/catch, using STORAGE_PREFIX to build the key and swallowing or
logging errors instead of allowing exceptions to propagate from
localStorage.setItem.

In `@README.md`:
- Line 34: Update the README line describing `PAYMENTS_EPHEMERAL_RPC_URL` and
`EPHEMERAL_RPC_URL` to expand the "ER" acronym for clarity—replace or augment
"ER" with "Ephemeral RPC" (or "Ephemeral RPC (ER)") so the sentence reads
something like: "`PAYMENTS_EPHEMERAL_RPC_URL` or `EPHEMERAL_RPC_URL`: ephemeral
RPC (ER) used when signed transactions must be submitted to Ephemeral RPC."
Reference the environment variable names `PAYMENTS_EPHEMERAL_RPC_URL` and
`EPHEMERAL_RPC_URL` when making the change.
- Line 34: Update the README description for the PAYMENTS_EPHEMERAL_RPC_URL and
EPHEMERAL_RPC_URL entries to state that Bearer authentication is required when
submitting signed transactions to the ephemeral RPC; explicitly note that
requests must include an Authorization: Bearer <token> header (or equivalent)
and mention that users must obtain/configure a valid token when setting those
environment variables so the endpoint will accept submissions.
- Around line 51-52: Remove the unintended trailing blank line after the
sentence describing the Handle tab so the paragraph ends immediately after
"private stealth-transfer route."; locate the sentence containing "Handle tab"
and ".block" and delete the empty line following it to clean up the README
formatting.

---

Outside diff comments:
In `@components/one/payment-card.tsx`:
- Around line 1339-1361: The dependency list includes isValidReceiver but the
effect body checks isStealthReceiver and resolvedReceiver directly; update the
effect callback so it uses isValidReceiver in the guard (e.g., replace the
current checks with if (!isValidReceiver) return;) and keep isValidReceiver in
the dependency array, ensuring the effect's logic references the derived value
rather than the underlying pieces (symbols to change: isValidReceiver,
isStealthReceiver, resolvedReceiver inside the useEffect callback and the
dependency array near the closing bracket).

In `@components/one/trade-hub.tsx`:
- Around line 179-230: trade-hub.tsx currently treats the presence of the "h"
query param (hasHandleSelection = Boolean(searchParams.get("h"))) as selecting
the handle tab but HandleCard ignores that param and instead uses
getStoredStealthHandle(owner); fix by wiring the "h" deep-link into HandleCard:
read searchParams.get("h") (or accept it as a prop from trade-hub) and, in
HandleCard's initialization logic (where it currently calls
getStoredStealthHandle(owner)), prefer the parsed "h" value to pre-populate the
input/selection (falling back to getStoredStealthHandle(owner) when "h" is
absent); alternatively, if "h" is not intended as a deep-link, remove/rename the
Boolean(searchParams.get("h")) check in trade-hub
(updateTabUrl/hasHandleSelection) to stop implying consumption — reference
symbols: hasHandleSelection, searchParams.get("h"), HandleCard,
getStoredStealthHandle(owner), and updateTabUrl.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7c3a5f0a-cd29-4850-95db-292ec60713a7

📥 Commits

Reviewing files that changed from the base of the PR and between e967b83 and ee32852.

📒 Files selected for processing (13)
  • README.md
  • app/api/payments/send/route.ts
  • app/api/payments/stealth-pool/route.ts
  • app/api/payments/transfer-queue/ensure-crank/route.ts
  • app/api/payments/transfer-stealth/route.ts
  • components/one/handle-card.tsx
  • components/one/payment-card.tsx
  • components/one/swap-card.tsx
  • components/one/trade-hub.tsx
  • lib/payment-transactions.ts
  • lib/payments.ts
  • lib/solana-rpc.ts
  • lib/stealth-handles.ts

Comment thread app/api/payments/send/route.ts
Comment thread app/api/payments/stealth-pool/route.ts
Comment thread lib/payment-transactions.ts
Comment thread lib/stealth-handles.ts
Comment thread README.md
Comment thread README.md
@magicblock-labs magicblock-labs deleted a comment from coderabbitai Bot Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant