From c7e8f628aa215caaa999f06d7fccb910fd5cc099 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sun, 15 Feb 2026 16:58:17 -0800 Subject: fix custodial bitcoin sends --- src/lib/parse.ts | 8 +----- .../(app)/send/bitcoin/[address]/+layout.server.ts | 11 +++++--- .../(app)/send/bitcoin/[address]/+page.svelte | 2 +- .../[address]/[amount]/[...feeRate]/+page.svelte | 2 +- src/routes/(app)/send/invoice/[id]/+page.server.ts | 10 ++++--- src/routes/(app)/send/invoice/[id]/+page.svelte | 31 +++++++++++++--------- 6 files changed, 36 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/lib/parse.ts b/src/lib/parse.ts index 48541728..06adfd7e 100644 --- a/src/lib/parse.ts +++ b/src/lib/parse.ts @@ -48,7 +48,7 @@ export default async (s, host, cookies) => { } const { validate } = await loadValidate(); - if (validate(t) || isLiquid(t)) { + if (validate(t) || isLiquid(t) || t.startsWith("bcrt1")) { let r = `/send/bitcoin/${t}`; if (amount) r += `/${Math.round(amount * sats)}`; redirect(307, r); @@ -123,10 +123,4 @@ export default async (s, host, cookies) => { } catch {} if (fund) redirect(307, `/send/fund/${t}`); - - try { - invoice ||= await get(`/invoice/${t}`); - } catch {} - - if (invoice) redirect(307, `/invoice/${invoice.id}`); }; diff --git a/src/routes/(app)/send/bitcoin/[address]/+layout.server.ts b/src/routes/(app)/send/bitcoin/[address]/+layout.server.ts index d8df1986..6acb36b6 100644 --- a/src/routes/(app)/send/bitcoin/[address]/+layout.server.ts +++ b/src/routes/(app)/send/bitcoin/[address]/+layout.server.ts @@ -15,10 +15,13 @@ export async function load({ cookies, params, parent }) { } if (invoice) { - const recipient = await get(`/users/${invoice.uid}`); - let r = `/pay/${recipient.username}`; - if (amount) r += `/${amount}`; - if (recipient?.id !== user.id) redirect(307, r); + const isVault = invoice.account?.pubkey || invoice.account?.seed; + if (isVault) { + // Vault account — stay on /send/bitcoin for on-chain send + } else { + // Custodial account — send internally via the original invoice + if (invoice.uid !== user.id) redirect(307, `/send/invoice/${invoice.id}`); + } } return { balance }; diff --git a/src/routes/(app)/send/bitcoin/[address]/+page.svelte b/src/routes/(app)/send/bitcoin/[address]/+page.svelte index 6a34ccfa..7aba37a2 100644 --- a/src/routes/(app)/send/bitcoin/[address]/+page.svelte +++ b/src/routes/(app)/send/bitcoin/[address]/+page.svelte @@ -48,7 +48,7 @@
-
diff --git a/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte index 1ef38f02..3afeeace 100644 --- a/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte +++ b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte @@ -74,7 +74,7 @@ return; } - if ((account.seed || data.user.seed) && !signed) (cancel(), void togglePassword()); + if ((account.seed || (data.user.seed && account.fingerprint)) && !signed) (cancel(), void togglePassword()); return async ({ result }) => { if (result.type === "redirect") { goto(result.location); diff --git a/src/routes/(app)/send/invoice/[id]/+page.server.ts b/src/routes/(app)/send/invoice/[id]/+page.server.ts index 59add224..dfbe9d26 100644 --- a/src/routes/(app)/send/invoice/[id]/+page.server.ts +++ b/src/routes/(app)/send/invoice/[id]/+page.server.ts @@ -12,7 +12,7 @@ export async function load({ cookies, depends, params: { id }, parent }) { const trust = await get("/trust", auth(cookies)); const trusted = trust.includes(invoice.uid); - if (trusted && (pin || !user.haspin)) { + if (trusted && (pin || !user.haspin) && invoice.amount) { let p; try { if (!invoice.tip && invoice.user.prompt) { @@ -37,8 +37,12 @@ export async function load({ cookies, depends, params: { id }, parent }) { if (invoice.memoPrompt && invoice.memo === null) redirect(307, `/invoice/${id}/memo`); if (user && invoice.aid === aid) error(500, { message: "Cannot send to self" }); - else if (user && ![types.lightning, types.bolt12].includes(invoice.type)) - redirect(307, `/send/${invoice.type === types.ecash ? "ecash" : "bitcoin"}/${invoice.hash}`); + else if (user && ![types.lightning, types.bolt12].includes(invoice.type)) { + // Don't redirect custodial bitcoin invoices — they should be paid internally + const isVault = invoice.account?.pubkey || invoice.account?.seed; + if (invoice.type !== types.bitcoin || isVault) + redirect(307, `/send/${invoice.type === types.ecash ? "ecash" : "bitcoin"}/${invoice.hash}`); + } if (!user) redirect(307, `/invoice/${id}`); diff --git a/src/routes/(app)/send/invoice/[id]/+page.svelte b/src/routes/(app)/send/invoice/[id]/+page.svelte index 99040ed6..01b4facd 100644 --- a/src/routes/(app)/send/invoice/[id]/+page.svelte +++ b/src/routes/(app)/send/invoice/[id]/+page.svelte @@ -1,6 +1,6 @@