diff options
| author | Adam Soltys <[email protected]> | 2024-09-12 15:23:45 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2024-09-17 09:19:25 -0700 |
| commit | 2114156be3f89a2ef4c5bd13fd6d7a51d8fbc135 (patch) | |
| tree | ae764021c0f9017493366862d42ea6cac904a76e /src/routes/(app)/send | |
| parent | 1f63848ca9fb01336f04b41434de1cc06a5b3d93 (diff) | |
| download | coinos-ui-2114156be3f89a2ef4c5bd13fd6d7a51d8fbc135.tar.gz coinos-ui-2114156be3f89a2ef4c5bd13fd6d7a51d8fbc135.zip | |
self custodial send
Diffstat (limited to 'src/routes/(app)/send')
5 files changed, 162 insertions, 18 deletions
diff --git a/src/routes/(app)/send/bitcoin/[address]/+page.server.ts b/src/routes/(app)/send/bitcoin/[address]/+page.server.ts new file mode 100644 index 00000000..3e56e15a --- /dev/null +++ b/src/routes/(app)/send/bitcoin/[address]/+page.server.ts @@ -0,0 +1,7 @@ +import { auth, get } from "$lib/utils"; + +export async function load({ cookies }) { + let account = cookies.get("account"); + let { balance } = await get(`/account/${account}`, auth(cookies)); + return { balance }; +} diff --git a/src/routes/(app)/send/bitcoin/[address]/+page.svelte b/src/routes/(app)/send/bitcoin/[address]/+page.svelte index 838c5c04..b656dc06 100644 --- a/src/routes/(app)/send/bitcoin/[address]/+page.svelte +++ b/src/routes/(app)/send/bitcoin/[address]/+page.svelte @@ -8,9 +8,9 @@ export let data; - let { rates, user } = data; + let { balance, rates, user } = data; let { address } = $page.params; - let { balance, currency, username } = user; + let { currency, username } = user; let amount = 0; let submit, fiat; diff --git a/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.server.ts b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.server.ts index 7efe6e49..33e96595 100644 --- a/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.server.ts +++ b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.server.ts @@ -1,17 +1,31 @@ import { fail, redirect } from "@sveltejs/kit"; -import { auth, post, fd } from "$lib/utils"; +import { auth, get, post, fd } from "$lib/utils"; export async function load({ params: { address, amount, feeRate }, cookies }) { + let account = cookies.get("account"); + try { - let { fee, fees, ourfee, hex } = await post( + let { fee, fees, inputs, ourfee, hex } = await post( "/bitcoin/fee", - { address, amount, feeRate }, + { address, amount, feeRate, account }, auth(cookies) ); - return { amount, address, fee, fees, feeRate, ourfee, hex }; + account = await get(`/account/${account}`, auth(cookies)); + + return { + account, + amount, + address, + fee, + fees, + feeRate, + ourfee, + hex, + inputs, + }; } catch (e: any) { - return { amount, address, feeRate, message: e.message }; + return { account, amount, address, feeRate, message: e.message }; } } @@ -22,7 +36,8 @@ export const actions = { request, }) => { try { - await post("/bitcoin/send", await fd(request), auth(cookies)); + let body = await fd(request); + await post("/bitcoin/send", body, auth(cookies)); } catch (e: any) { console.log("problem sending bitcoin", e); return fail(400, { address, amount, feeRate, message: e.message }); 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 84506e6b..1267a992 100644 --- a/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte +++ b/src/routes/(app)/send/bitcoin/[address]/[amount]/[...feeRate]/+page.svelte @@ -1,20 +1,84 @@ <script> + import { tick } from "svelte"; import { t } from "$lib/translations"; import { enhance } from "$app/forms"; import Toggle from "$comp/Toggle.svelte"; import Icon from "$comp/Icon.svelte"; import Spinner from "$comp/Spinner.svelte"; import { page } from "$app/stores"; - import { fiat as toFiat, f, focus, s, sat, closest } from "$lib/utils"; + import { fiat as toFiat, f, focus, s, sat, closest, network } from "$lib/utils"; import { pin } from "$lib/store"; import { goto, invalidate } from "$app/navigation"; import { rate } from "$lib/store"; + import { applyAction } from "$app/forms"; + import { hex as hexUtil } from "@scure/base"; + import * as btc from "@scure/btc-signer"; + import { decrypt } from "nostr-tools/nip49"; + import { HDKey } from "@scure/bip32"; + import { entropyToMnemonic, mnemonicToSeed } from "@scure/bip39"; + import { wordlist } from "@scure/bip39/wordlists/english"; export let data; export let form; - let { amount, address, message, fee, fees, feeRate, ourfee, rates, hex } = - data; + let { encode, decode } = hexUtil; + let revealPassword = false; + let passwordPrompt; + let password; + let cancel = () => (passwordPrompt = false); + let signed; + + let togglePassword = () => (passwordPrompt = !passwordPrompt); + let handler = ({ cancel }) => { + if (account.seed && !signed) cancel(), togglePassword(); + return async ({ result }) => { + if (result.type === "redirect") { + goto(result.location); + } else { + await applyAction(result); + } + }; + }; + + let signTx = async () => { + let entropy = await decrypt(account.seed, password); + let mnemonic = entropyToMnemonic(entropy, wordlist); + let seed = await mnemonicToSeed(mnemonic); + let master = HDKey.fromMasterSeed(seed, network); + let child = master.derive("m/84'/0'/0'"); + + let tx = btc.Transaction.fromRaw(decode(hex)); + + for (let [i, input] of tx.inputs.entries()) { + let { nonWitnessUtxo, path } = inputs[i]; + tx.updateInput(i, { nonWitnessUtxo }); + let key = child.derive(path); + let { privateKey, publicKey } = key; + let address = btc.getAddress("wpkh", privateKey, network); + tx.signIdx(privateKey, i); + } + tx.finalize(); + hex = tx.hex; + signed = true; + await tick(); + submit.click(); + }; + + let toggle = () => (submitting = !submitting); + + let { + account, + amount, + address, + message, + fee, + fees, + feeRate, + ourfee, + rates, + hex, + inputs, + } = data; $: reload(data); let reload = () => { @@ -115,10 +179,12 @@ </div> {/if} - <form method="POST" use:enhance on:submit={() => (submitting = true)}> + <form method="POST" use:enhance={handler}> <input name="pin" value={$pin} type="hidden" /> <input name="hex" value={hex} type="hidden" /> <input name="rate" value={$rate} type="hidden" /> + <input name="account" value={account.id} type="hidden" /> + <input name="signed" value={signed} type="hidden" /> <div class="flex justify-center gap-2"> <button @@ -139,6 +205,68 @@ {/if} </div> +{#if passwordPrompt} + <div + class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-20" + > + <div + class="relative top-1/3 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white space-y-5" + > + <h1 class="text-center text-2xl font-semibold"> + {$t("payments.enterWalletPass")} + </h1> + <form on:submit|preventDefault={signTx}> + <div class="relative mb-5"> + {#if revealPassword} + <input + name="password" + type="text" + required + class="bg-primary" + bind:value={password} + autocapitalize="none" + use:focus + /> + {:else} + <input + name="password" + type="password" + required + class="bg-primary" + bind:value={password} + autocapitalize="none" + use:focus + /> + {/if} + <button + type="button" + on:click={() => (revealPassword = !revealPassword)} + class="absolute right-5 top-6" + > + <Icon icon={revealPassword ? "eye" : "eye-off"} /> + </button> + </div> + <div class="w-full flex"> + <button + type="button" + class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80 mx-auto" + on:click={cancel} + on:keydown={cancel} + > + <div class="my-auto">Cancel</div> + </button> + <button + type="submit" + class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80 mx-auto" + > + <div class="my-auto">Submit</div> + </button> + </div> + </form> + </div> + </div> +{/if} + <style> .no-transition { view-transition-name: fee; diff --git a/src/routes/(app)/send/ecash/+page.svelte b/src/routes/(app)/send/ecash/+page.svelte index 15c7c5d3..615c068a 100644 --- a/src/routes/(app)/send/ecash/+page.svelte +++ b/src/routes/(app)/send/ecash/+page.svelte @@ -29,12 +29,6 @@ let toggle = () => (submitting = !submitting); </script> -<a href="/send"> - <button type="button" class="ml-5 md:ml-20 mt-5 md:mt-10 hover:opacity-80"> - <Icon icon="arrow-left" style="w-10" /> - </button> -</a> - <div class="container px-4 max-w-xl mx-auto space-y-5 text-center"> <h1 class="text-3xl md:text-4xl font-semibold mb-2"> {$t("payments.createEcash")} |
