Conversation
There was a problem hiding this comment.
Pull request overview
Implements a new “private deposit” flow (UI + background handler) to move funds from a public Stellar account into a private channel by creating multiple UTXOs based on a chosen entropy (privacy) level.
Changes:
- Added a background
DEPOSITmessage + handler that reserves UTXOs, partitions amounts, builds Moonlight operations, and submits a bundle to the privacy provider. - Added popup deposit + deposit review pages/templates and wired navigation from the private Home view.
- Added utilities and client improvements (random partition helper; privacy provider client auth/bundle submission updates).
Reviewed changes
Copilot reviewed 17 out of 18 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/popup/templates/home-template.tsx | Adds private-view action buttons and hooks “Ramp” button into deposit start. |
| src/popup/pages/home-page.tsx | Wires onStartDeposit to popup state navigation. |
| src/popup/hooks/state.tsx | Adds deposit/deposit-review routes and persists deposit form data. |
| src/popup/app.tsx | Routes deposit pages. |
| src/popup/api/deposit.ts | Adds popup API wrapper to call background deposit handler. |
| src/popup/pages/deposit-page.tsx | Implements deposit form page (loads channel/provider/session and public balance). |
| src/popup/pages/deposit-review-page.tsx | Implements deposit review + execution page. |
| src/popup/templates/deposit-form-template.tsx | Deposit form UI (method, amount, entropy). |
| src/popup/templates/deposit-review-template.tsx | Review/confirm UI showing details and operations breakdown. |
| src/background/messages.ts | Adds MessageType.Deposit and payload/response typing. |
| src/background/handler.ts | Registers the deposit handler. |
| src/background/handlers/private/deposit.types.ts | Introduces DepositRequest/Response, method + entropy enums. |
| src/background/handlers/private/deposit.ts | Implements backend private deposit flow (UTXO reservation, op creation, provider submission). |
| src/background/utils/random-partition.ts | Adds cryptographically-secure random amount partitioning utility. |
| src/background/services/privacy-provider-client.ts | Improves auth token validation, normalizes postAuth response, adds bundle submission with auth error mapping. |
| src/background/handlers/private/connect-privacy-provider.ts | Adds token validation and logs when saving provider sessions. |
| deno.lock | Updates dependency lockfile (includes @types/node and various bumps). |
| deno.json | Minor formatting changes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| useMemo(() => { | ||
| if (!formData) return; | ||
| getPrivateChannels({ network }) | ||
| .then((res) => { | ||
| if (res.ok) { | ||
| setPrivateChannels({ | ||
| channels: res.channels, | ||
| selectedChannelId: res.selectedChannelId, | ||
| }); | ||
| } | ||
| }) | ||
| .catch((err) => { | ||
| console.error("Failed to load private channels", err); | ||
| }); | ||
| }, [network, formData]); |
There was a problem hiding this comment.
Side effects (fetching private channels + setState) are being executed inside useMemo. useMemo is not guaranteed to run exactly once per dependency change and should be pure; this can cause repeated network calls during render. Move this logic to useEffect and keep useMemo only for derived values.
| // Calculate estimated fee (simplified - in production this should come from the backend) | ||
| const estimatedFee = useMemo(() => { | ||
| // Rough estimate: 0.00001 XLM per UTXO | ||
| return (utxoCount * 0.00001).toFixed(7); | ||
| }, [utxoCount]); |
There was a problem hiding this comment.
The fee shown to the user is calculated as utxoCount * 0.00001, but the backend deposit handler uses a fixed fee schedule per entropy level (e.g., 0.05/0.25/0.5/0.75 XLM). This will display an incorrect total on the review screen and can mislead users about the amount debited. Use the same fee table as the backend or fetch an exact quote from the backend and display that here.
| const entropyLevels: EntropyLevel[] = ["LOW", "MEDIUM", "HIGH", "V_HIGH"]; | ||
|
|
||
| return ( | ||
| <SubpageShell title="Ramp" onBack={props.onBack}> |
There was a problem hiding this comment.
The subpage title is set to "Ramp", but this component is used for the deposit flow. Update the title (and any related copy) to "Deposit"/"Deposit Funds" so navigation and headings match the actual action.
| <SubpageShell title="Ramp" onBack={props.onBack}> | |
| <SubpageShell title="Deposit Funds" onBack={props.onBack}> |
| { | ||
| icon: IconCoinFilled, | ||
| label: "Ramp", | ||
| key: "ramp", | ||
| onClick: () => { | ||
| if (!canStartDeposit) return; | ||
| if ( | ||
| !selectedPrivateChannel | ||
| ?.id || | ||
| !selectedPrivateChannel | ||
| ?.selectedProviderId | ||
| ) { | ||
| return; | ||
| } | ||
| props.onStartDeposit?.( | ||
| selectedPrivateChannel.id, | ||
| selectedPrivateChannel | ||
| .selectedProviderId!, | ||
| ); | ||
| }, |
There was a problem hiding this comment.
This action is labeled "Ramp" but triggers onStartDeposit(...). Rename the label/key to reflect the actual behavior (e.g., "Deposit"), otherwise users and future maintainers will assume it opens an on-ramp flow rather than the deposit flow.
| const { | ||
| network, | ||
| channelId, | ||
| providerId, | ||
| accountId, | ||
| amount, | ||
| entropyLevel, | ||
| } = message as DepositRequest & { type: MessageType.Deposit }; |
There was a problem hiding this comment.
DepositRequest includes a method, but the handler ignores it entirely. Since the UI can select "3RD-PARTY RAMP" (even if disabled today) and the API type allows it, the handler should validate method and explicitly reject unsupported values (e.g., return an error code like UNSUPPORTED_METHOD) instead of silently treating everything as DIRECT.
| // Convert human-readable amount and fee to BigInt | ||
| const parsedAmount = Number.parseFloat(amount); | ||
| if (!Number.isFinite(parsedAmount) || parsedAmount <= 0) { | ||
| throw new Error("Invalid deposit amount"); | ||
| } | ||
|
|
||
| const feeAmount = getFeeForEntropyLevel(entropyLevel); | ||
| const totalAmount = fromDecimals(parsedAmount + feeAmount, 7); | ||
|
|
||
| const amountBigInt = fromDecimals(parsedAmount, 7); |
There was a problem hiding this comment.
Fee/amount arithmetic is done in JS number space (parseFloat, feeAmount number, then parsedAmount + feeAmount). Using floating point for monetary amounts can introduce rounding errors at 7-decimal precision. Prefer doing all calculations in integer space (e.g., convert amount and fee to base units as bigint and add) to guarantee exact stroop values.
| // Convert human-readable amount and fee to BigInt | |
| const parsedAmount = Number.parseFloat(amount); | |
| if (!Number.isFinite(parsedAmount) || parsedAmount <= 0) { | |
| throw new Error("Invalid deposit amount"); | |
| } | |
| const feeAmount = getFeeForEntropyLevel(entropyLevel); | |
| const totalAmount = fromDecimals(parsedAmount + feeAmount, 7); | |
| const amountBigInt = fromDecimals(parsedAmount, 7); | |
| // Convert human-readable amount and fee to BigInt, and do all math in integer space. | |
| const amountBigInt = fromDecimals(amount, 7); | |
| if (amountBigInt <= 0n) { | |
| throw new Error("Invalid deposit amount"); | |
| } | |
| const feeAmount = getFeeForEntropyLevel(entropyLevel); | |
| const feeBigInt = fromDecimals(feeAmount.toFixed(7), 7); | |
| const totalAmount = amountBigInt + feeBigInt; |
| console.log("operationsMLXDR", operationsMLXDR); | ||
|
|
There was a problem hiding this comment.
console.log("operationsMLXDR", operationsMLXDR) logs the full operation payloads. These XDRs can contain sensitive transaction details and will also spam logs in production. Remove this log or gate it behind the existing DEV flag / a debug logger that can be disabled.
| console.log("operationsMLXDR", operationsMLXDR); |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@copilot open a new pull request to apply changes based on the comments in this thread |
Implement Private Deposit Functionality
This PR implements the private deposit flow, allowing users to deposit public funds into private channels by creating UTXOs with configurable privacy levels. The implementation follows the same patterns as the receive flow and aligns with the sandbox reference implementation.
Overview
Users can now deposit funds from their public Stellar account into a private channel by specifying:
The system will:
Features
Deposit Form: Input screen for specifying deposit parameters
Deposit Review: Confirmation screen showing transaction details
Home Integration: Deposit button in private view navigates to the deposit flow
Technical Changes
Backend
Handler:
src/background/handlers/private/deposit.tsUtxoBasedStellarAccount(count based on entropy level)partitionAmountRandomutilityMoonlightOperation.depositoperation with conditionsMoonlightOperation.createoperations for each UTXOPrivacyProviderClientTypes:
src/background/handlers/private/deposit.types.tsDepositRequest: network, channelId, providerId, accountId, method, amount, entropyLevelDepositResponse: ok, id, hash, errorDepositMethod: "DIRECT" | "3RD-PARTY RAMP"EntropyLevel: "LOW" | "MEDIUM" | "HIGH" | "V_HIGH"Message system: Added
MessageType.Depositto message routingFrontend
API service:
src/popup/api/deposit.tsPages:
src/popup/pages/deposit-page.tsx: Main deposit form pagesrc/popup/pages/deposit-review-page.tsx: Review and confirmation pageTemplates:
src/popup/templates/deposit-form-template.tsx: Reusable form componentsrc/popup/templates/deposit-review-template.tsx: Review componentState management: Added deposit routes and state handling
deposit,deposit-reviewgoDeposit,goDepositReview,setDepositFormData,clearDepositFormDataNavigation: Integrated deposit button in
HomeTemplateprivate viewImplementation Details
Entropy levels: Configurable privacy levels that determine UTXO count
Amount partitioning: Uses cryptographically secure random distribution via
partitionAmountRandomFee calculation: Fees are based on entropy level and added to the total amount
UTXO reservation: Automatically derives and reserves enough UTXOs for the selected entropy level
Operation structure: Creates one DEPOSIT operation with conditions from multiple CREATE operations
Expiration handling: Calculates transaction expiration using RPC ledger sequence
Error handling: Comprehensive error codes (LOCKED, NOT_FOUND, SESSION_NOT_FOUND, INVALID_SESSION, AUTH_FAILED, DEPOSIT_FAILED)
Session management: Automatically clears provider session on authentication errors
Type safety: Full TypeScript typing throughout
Consistency: Follows same patterns as receive flow for maintainability
Files Changed
New Files:
src/background/handlers/private/deposit.tssrc/background/handlers/private/deposit.types.tssrc/popup/api/deposit.tssrc/popup/pages/deposit-page.tsxsrc/popup/pages/deposit-review-page.tsxsrc/popup/templates/deposit-form-template.tsxsrc/popup/templates/deposit-review-template.tsxModified Files:
src/background/messages.ts- Added Deposit message typesrc/background/handler.ts- Registered deposit handlersrc/popup/hooks/state.tsx- Added deposit routes and statesrc/popup/app.tsx- Added deposit route handlingsrc/popup/pages/home-page.tsx- Connected deposit buttonsrc/popup/templates/home-template.tsx- Added deposit action propTesting Notes
Future Enhancements