feat: add stealth address#18
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
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 valueStale dependency:
isValidReceiveris in deps but not used in callback body.The guard at lines 1251-1252 checks
isStealthReceiverandresolvedReceiverdirectly rather than usingisValidReceiver. SinceisValidReceiveris derived from those values, this is functionally correct but the dependency is misleading. Consider either:
- Remove
isValidReceiverfrom deps (it's derived from values already in deps)- Use
isValidReceiverin 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 | 🔵 TrivialAlign
hquery param usage withHandleCardbehavior
trade-hub.tsxtreats the presence of thehquery param as a signal to activate thehandletab (hasHandleSelection = Boolean(searchParams.get("h"))), butHandleCardnever readssearchParams.get("h")and instead initializeshandlefromgetStoredStealthHandle(owner).
Ifhis meant for deep-linking a specific handle, parsesearchParams.get("h")inHandleCardand 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
📒 Files selected for processing (13)
README.mdapp/api/payments/send/route.tsapp/api/payments/stealth-pool/route.tsapp/api/payments/transfer-queue/ensure-crank/route.tsapp/api/payments/transfer-stealth/route.tscomponents/one/handle-card.tsxcomponents/one/payment-card.tsxcomponents/one/swap-card.tsxcomponents/one/trade-hub.tsxlib/payment-transactions.tslib/payments.tslib/solana-rpc.tslib/stealth-handles.ts
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/one/handle-card.tsx (1)
304-331:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRetry after partial success may cause duplicate or wasted transactions.
If
ensureTransactionsucceeds (line 305-308) butupdateTransactionfails with blockhash expiration, the retry loop rebuilds and resubmits both transactions. This could:
- Submit a duplicate ensure transaction (wasting fees if idempotent, or causing errors if not)
- Leave the user confused about which transaction to track
Consider tracking ensure success within the loop and skipping its resubmission on retry:
Proposed fix
let body: StealthPoolBuildResponse | null = null; let nextSignature = ""; + let ensureAlreadySubmitted = false; for (let attempt = 0; attempt < 2; attempt += 1) { try { // ... building and signing ... setStatus("sending"); - const ensureSignature = await submitSignedPaymentTransaction( - ensureTransaction, - signedSetupTransaction - ); - setSignature(ensureSignature); + if (!ensureAlreadySubmitted) { + const ensureSignature = await submitSignedPaymentTransaction( + ensureTransaction, + signedSetupTransaction + ); + setSignature(ensureSignature); + ensureAlreadySubmitted = true; + } nextSignature = await submitSignedPaymentTransaction( updateTransaction, signedUpdatePoolTransaction, authToken ); // ... } catch (err) { // ... if (attempt === 0 && isPaymentBlockhashExpiredError(err)) { - setSignature(null); + // Keep signature if ensure already submitted + if (!ensureAlreadySubmitted) { + setSignature(null); + } continue; } throw err; } }🤖 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/handle-card.tsx` around lines 304 - 331, The retry loop may resubmit both ensureTransaction and updateTransaction when only updateTransaction fails with blockhash expiration, potentially wasting fees on duplicate ensure transaction submissions. Track whether the ensureTransaction was successfully submitted (when setSignature is called) using a flag at the beginning of the while loop, and on retry, skip the ensureTransaction submission if it already succeeded in a previous attempt. Only resubmit updateTransaction on the blockhash expiration retry.
🤖 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.
Outside diff comments:
In `@components/one/handle-card.tsx`:
- Around line 304-331: The retry loop may resubmit both ensureTransaction and
updateTransaction when only updateTransaction fails with blockhash expiration,
potentially wasting fees on duplicate ensure transaction submissions. Track
whether the ensureTransaction was successfully submitted (when setSignature is
called) using a flag at the beginning of the while loop, and on retry, skip the
ensureTransaction submission if it already succeeded in a previous attempt. Only
resubmit updateTransaction on the blockhash expiration retry.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: be50aba5-769b-4018-bbe4-1edbdd1a78ec
📒 Files selected for processing (2)
components/one/handle-card.tsxlib/stealth-handles.ts
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds ChangesStealth Handles and Private Payments
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/one/trade-hub.tsx (1)
118-129:⚠️ Potential issue | 🟠 Major | ⚡ Quick winMake
?h=both win tab selection and prefill the handle.
hasHandleSelectionis checked after payment/request/shield params, and<HandleCard />receives nohvalue. A URL like?h=alice.block&rcv=...opens Payment, while?h=alice.blockopens Handle but shows the stored/blank handle instead of the URL handle.Proposed direction
const hasHandleSelection = Boolean(searchParams.get("h")); + const initialHandle = searchParams.get("h") ?? ""; const [activeTop, setActiveTop] = useState<TopTab>( selectableUrlTab ? selectableUrlTab + : hasHandleSelection + ? "handle" : hasPaymentSelection ? "payment" @@ - : hasHandleSelection - ? "handle" - : hasSwapSelection && !isSwapDisabled + : hasSwapSelection && !isSwapDisabled @@ - if (hasPaymentSelection) { + if (hasHandleSelection) { + setActiveTop("handle"); + return; + } + + if (hasPaymentSelection) { setActiveTop("payment"); return; } @@ - if (hasHandleSelection) { - setActiveTop("handle"); - return; - } - @@ - {activeTop === "handle" && <HandleCard />} + {activeTop === "handle" && <HandleCard initialHandle={initialHandle} />}This also requires adding an
initialHandleprop incomponents/one/handle-card.tsxand using it when the wallet owner initializes the local handle state.Also applies to: 143-160, 315-315
🤖 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 118 - 129, The handle parameter (?h=) doesn't properly win tab selection and doesn't prefill the handle input because hasHandleSelection is checked last in the conditional chain, allowing other parameters to take precedence. Fix this by reordering the tab selection logic in the useState initialization so hasHandleSelection is checked earlier with higher priority than the payment, request, and shield checks. Additionally, extract the handle value from searchParams.get("h") and pass it as an initialHandle prop to the HandleCard component. Then add an initialHandle prop to the HandleCard component definition in handle-card.tsx and use this value when initializing the local handle state.
🤖 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/stealth-pool/route.ts`:
- Line 84: Wrap the `request.json()` call in a try-catch block to handle JSON
parsing errors separately. When `request.json()` fails to parse the incoming
payload (due to malformed JSON), catch that error and return a 400 Bad Request
response with an appropriate error message instead of allowing it to be caught
by the generic error handler. This ensures client input errors are properly
classified as 400 errors rather than 500 server errors.
In `@components/one/handle-card.tsx`:
- Around line 414-423: The input elements for handle and owner-key are missing
accessible names, making them inaccessible to screen reader users. Add
aria-label attributes to both input elements to provide descriptive accessible
names. For the handle input (the one with placeholder "e.g satoshi.block,
nakamoto.block, etc"), add an appropriate aria-label describing what should be
entered. Similarly, add an aria-label to the owner-key input mentioned at lines
473-480. Choose descriptive labels that clearly indicate the purpose of each
input field.
- Around line 276-279: The build request error handling in the code snippet
starting at line 276 throws a generic Error when res.ok is false, but does not
clear stale auth tokens when the response status is 401. The 401 token cleanup
logic exists elsewhere but only applies to transaction-submission errors. Before
throwing the Error in the !res.ok condition, check if res.status is 401 and if
so, perform the same auth token cleanup that occurs at line 319 for
transaction-submission failures (such as removing the stored token from
localStorage). This ensures stale tokens are cleared regardless of whether the
401 comes from the build request or transaction submission.
- Around line 579-582: The disabled condition for the button in the disabled
prop only checks for !signMessage, but the saveHandle function returns early
when !signTransaction, causing wallets without transaction signing capability to
display an enabled button that cannot function. Add !signTransaction to the
existing disabled condition alongside the other checks (isBusy, isValidHandle,
hasValidDestinations, and signMessage) to ensure the button is properly disabled
when the wallet cannot sign transactions.
In `@components/one/payment-card.tsx`:
- Around line 752-755: The useEffect hook currently only sets isPrivate to true
when a stealth receiver is detected, but handleSend always sends fromBalance:
"base" and toBalance: "base" for stealth receivers, creating a UI mismatch. When
isStealthReceiver becomes true and isPrivate is false, in addition to setting
isPrivate to true, also reset the routing delays to 0 and ensure that the
fromBalance and toBalance states are set to "base" to align the UI state with
what will actually be submitted in the payload. This prevents the UI from
showing a different route than what gets sent.
- Around line 39-44: The imported type UnsignedPaymentTransaction from
`@/lib/payment-transactions` collides with a local interface declaration with the
same name around lines 90-102, causing a duplicate declaration error. Alias the
imported UnsignedPaymentTransaction type to a different name in the import
statement (such as SharedUnsignedPaymentTransaction), then update the local
interface to extend this aliased type while adding the component-specific fields
from and sendRpcEndpoint.
---
Outside diff comments:
In `@components/one/trade-hub.tsx`:
- Around line 118-129: The handle parameter (?h=) doesn't properly win tab
selection and doesn't prefill the handle input because hasHandleSelection is
checked last in the conditional chain, allowing other parameters to take
precedence. Fix this by reordering the tab selection logic in the useState
initialization so hasHandleSelection is checked earlier with higher priority
than the payment, request, and shield checks. Additionally, extract the handle
value from searchParams.get("h") and pass it as an initialHandle prop to the
HandleCard component. Then add an initialHandle prop to the HandleCard component
definition in handle-card.tsx and use this value when initializing the local
handle state.
🪄 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: be347808-044a-402a-944f-402c6d53eac9
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (14)
README.mdapp/api/payments/send/route.tsapp/api/payments/stealth-pool/route.tsapp/api/payments/transfer-queue/ensure-crank/route.tsapp/api/payments/transfer/route.tscomponents/one/handle-card.tsxcomponents/one/payment-card.tsxcomponents/one/swap-card.tsxcomponents/one/trade-hub.tsxlib/payment-transactions.tslib/payments.tslib/solana-rpc.tslib/stealth-handles.tspackage.json
| ); | ||
| } | ||
|
|
||
| const body = (await request.json()) as StealthPoolBuildRequest; |
There was a problem hiding this comment.
Return 400 for malformed JSON payloads in POST.
request.json() parse errors currently fall through to the generic 500 catch, so client input errors are misclassified as server failures.
Suggested fix
- const body = (await request.json()) as StealthPoolBuildRequest;
+ let body: StealthPoolBuildRequest;
+ try {
+ body = (await request.json()) as StealthPoolBuildRequest;
+ } catch {
+ return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const body = (await request.json()) as StealthPoolBuildRequest; | |
| let body: StealthPoolBuildRequest; | |
| try { | |
| body = (await request.json()) as StealthPoolBuildRequest; | |
| } catch { | |
| return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 }); | |
| } |
🤖 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 `@app/api/payments/stealth-pool/route.ts` at line 84, Wrap the `request.json()`
call in a try-catch block to handle JSON parsing errors separately. When
`request.json()` fails to parse the incoming payload (due to malformed JSON),
catch that error and return a 400 Bad Request response with an appropriate error
message instead of allowing it to be caught by the generic error handler. This
ensures client input errors are properly classified as 400 errors rather than
500 server errors.
| const responseBody = await res.json().catch(() => null); | ||
| if (!res.ok) { | ||
| throw new Error(responseBody?.error || `Build failed: ${res.status}`); | ||
| } |
There was a problem hiding this comment.
Clear stale auth tokens when the build request returns 401.
Line 277 throws a plain Error for /api/payments/stealth-pool failures, so the 401 cleanup at Line 319 only runs for transaction-submission errors. A stale stored token will keep being reused and the user cannot save until localStorage is manually cleared.
Proposed fix
const responseBody = await res.json().catch(() => null);
if (!res.ok) {
+ if (res.status === 401) {
+ clearStoredPrivateAuthToken(owner);
+ if (attempt === 0) {
+ setSignature(null);
+ continue;
+ }
+ }
throw new Error(responseBody?.error || `Build failed: ${res.status}`);
}Also applies to: 317-323
🤖 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/handle-card.tsx` around lines 276 - 279, The build request
error handling in the code snippet starting at line 276 throws a generic Error
when res.ok is false, but does not clear stale auth tokens when the response
status is 401. The 401 token cleanup logic exists elsewhere but only applies to
transaction-submission errors. Before throwing the Error in the !res.ok
condition, check if res.status is 401 and if so, perform the same auth token
cleanup that occurs at line 319 for transaction-submission failures (such as
removing the stored token from localStorage). This ensures stale tokens are
cleared regardless of whether the 401 comes from the build request or
transaction submission.
| <input | ||
| type="text" | ||
| value={handle} | ||
| onChange={(event) => { | ||
| setHandle(event.target.value); | ||
| resetResultState(); | ||
| }} | ||
| placeholder="e.g satoshi.block, nakamoto.block, etc" | ||
| className="w-full bg-transparent font-mono text-lg text-foreground placeholder:text-muted-foreground/40 outline-none" | ||
| /> |
There was a problem hiding this comment.
Add accessible names to the handle and owner-key inputs.
These inputs are the primary controls for this flow but are not associated with labels, so screen-reader users cannot tell what to enter.
Proposed fix
<input
type="text"
+ aria-label="Stealth handle"
value={handle}
onChange={(event) => {
setHandle(event.target.value);
resetResultState();
}}
@@
<input
type="text"
+ aria-label={`Backing owner key ${index + 1}`}
value={destination}
onChange={(event) =>
updateDestination(index, event.target.value)
}Also applies to: 473-480
🧰 Tools
🪛 React Doctor (0.5.6)
[warning] 414-414: Blind users can't tell what this control does because screen readers find no label, so add visible text, aria-label, or aria-labelledby.
Give every interactive control a label screen readers can read.
(control-has-associated-label)
[warning] 414-414: This JSX crashes because React isn't in scope.
If you're on React 17+ with the new JSX transform, disable this rule. Otherwise import React at the top of the file.
(react-in-jsx-scope)
🤖 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/handle-card.tsx` around lines 414 - 423, The input elements
for handle and owner-key are missing accessible names, making them inaccessible
to screen reader users. Add aria-label attributes to both input elements to
provide descriptive accessible names. For the handle input (the one with
placeholder "e.g satoshi.block, nakamoto.block, etc"), add an appropriate
aria-label describing what should be entered. Similarly, add an aria-label to
the owner-key input mentioned at lines 473-480. Choose descriptive labels that
clearly indicate the purpose of each input field.
Source: Linters/SAST tools
| disabled={ | ||
| connected && | ||
| (isBusy || !isValidHandle || !hasValidDestinations || !signMessage) | ||
| } |
There was a problem hiding this comment.
Disable Save when the wallet cannot sign transactions.
saveHandle returns early when !signTransaction, but the button only disables for !signMessage; wallets without transaction signing will see an enabled button that does nothing.
Proposed fix
disabled={
connected &&
- (isBusy || !isValidHandle || !hasValidDestinations || !signMessage)
+ (isBusy ||
+ !isValidHandle ||
+ !hasValidDestinations ||
+ !signMessage ||
+ !signTransaction)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| disabled={ | |
| connected && | |
| (isBusy || !isValidHandle || !hasValidDestinations || !signMessage) | |
| } | |
| disabled={ | |
| connected && | |
| (isBusy || | |
| !isValidHandle || | |
| !hasValidDestinations || | |
| !signMessage || | |
| !signTransaction) | |
| } |
🤖 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/handle-card.tsx` around lines 579 - 582, The disabled
condition for the button in the disabled prop only checks for !signMessage, but
the saveHandle function returns early when !signTransaction, causing wallets
without transaction signing capability to display an enabled button that cannot
function. Add !signTransaction to the existing disabled condition alongside the
other checks (isBusy, isValidHandle, hasValidDestinations, and signMessage) to
ensure the button is properly disabled when the wallet cannot sign transactions.
| import { | ||
| PaymentTransactionSubmissionError, | ||
| type UnsignedPaymentTransaction, | ||
| deserializeUnsignedPaymentTransaction, | ||
| ensurePaymentTransferQueueCrank, | ||
| } from "@/lib/payment-transactions"; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Show the imported and local UnsignedPaymentTransaction declarations in this module.
sed -n '35,105p' components/one/payment-card.tsxRepository: magicblock-labs/one
Length of output: 1985
Resolve the UnsignedPaymentTransaction type collision.
This module imports type UnsignedPaymentTransaction from @/lib/payment-transactions (lines 40-44) and declares a local interface with the same name (lines 90-102), which TypeScript rejects as a duplicate declaration. Alias the shared type and extend it locally for the additional fields used here (from and sendRpcEndpoint).
Proposed fix
import {
PaymentTransactionSubmissionError,
- type UnsignedPaymentTransaction,
+ type UnsignedPaymentTransaction as BaseUnsignedPaymentTransaction,
deserializeUnsignedPaymentTransaction,
ensurePaymentTransferQueueCrank,
} from "`@/lib/payment-transactions`";
@@
-interface UnsignedPaymentTransaction {
- kind: string;
- version?: "legacy" | "v0" | 0 | "0";
- transactionBase64: string;
- sendTo: "base" | "ephemeral";
+interface UnsignedPaymentTransaction extends BaseUnsignedPaymentTransaction {
from?: "base" | "ephemeral";
- recentBlockhash: string;
- lastValidBlockHeight: number;
- instructionCount: number;
- requiredSigners: string[];
- validator?: string;
sendRpcEndpoint?: string;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { | |
| PaymentTransactionSubmissionError, | |
| type UnsignedPaymentTransaction, | |
| deserializeUnsignedPaymentTransaction, | |
| ensurePaymentTransferQueueCrank, | |
| } from "@/lib/payment-transactions"; | |
| import { | |
| PaymentTransactionSubmissionError, | |
| type UnsignedPaymentTransaction as BaseUnsignedPaymentTransaction, | |
| deserializeUnsignedPaymentTransaction, | |
| ensurePaymentTransferQueueCrank, | |
| } from "`@/lib/payment-transactions`"; |
🤖 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 39 - 44, The imported type
UnsignedPaymentTransaction from `@/lib/payment-transactions` collides with a local
interface declaration with the same name around lines 90-102, causing a
duplicate declaration error. Alias the imported UnsignedPaymentTransaction type
to a different name in the import statement (such as
SharedUnsignedPaymentTransaction), then update the local interface to extend
this aliased type while adding the component-specific fields from and
sendRpcEndpoint.
| useEffect(() => { | ||
| if (!isStealthReceiver || isPrivate) return; | ||
| setIsPrivate(true); | ||
| }, [isStealthReceiver, isPrivate]); |
There was a problem hiding this comment.
Keep stealth UI state aligned with the base/base payload.
For stealth receivers, handleSend always sends fromBalance: "base" and toBalance: "base", but the effect only flips isPrivate. If the user previously selected shielded source/destination, the UI can show one route while the submitted payload uses another; entering a stealth handle from public mode also leaves routing delays at 0.
Proposed fix
useEffect(() => {
- if (!isStealthReceiver || isPrivate) return;
- setIsPrivate(true);
- }, [isStealthReceiver, isPrivate]);
+ if (!isStealthReceiver) return;
+ if (!isPrivate) setIsPrivate(true);
+ if (sourceBalance !== "base") setSourceBalance("base");
+ if (recipientBalance !== "base") setRecipientBalance("base");
+ if (minDelayMs === 0 && maxDelayMs === 0) {
+ setMinDelayMs(DEFAULT_MIN_DELAY_MS);
+ setMaxDelayMs(DEFAULT_MAX_DELAY_MS);
+ }
+ }, [
+ isStealthReceiver,
+ isPrivate,
+ sourceBalance,
+ recipientBalance,
+ minDelayMs,
+ maxDelayMs,
+ ]);Also applies to: 1748-1749
🤖 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 752 - 755, The useEffect hook
currently only sets isPrivate to true when a stealth receiver is detected, but
handleSend always sends fromBalance: "base" and toBalance: "base" for stealth
receivers, creating a UI mismatch. When isStealthReceiver becomes true and
isPrivate is false, in addition to setting isPrivate to true, also reset the
routing delays to 0 and ensure that the fromBalance and toBalance states are set
to "base" to align the UI state with what will actually be submitted in the
payload. This prevents the UI from showing a different route than what gets
sent.

Create user-handle:
Now the destination can be user-handle as well:
Summary by CodeRabbit
New Features
.block) support for payment recipients alongside wallet addresses and.solnamesDocumentation
Chores