From 97b6e85acda42a2b41180e309931fe432c4415bf Mon Sep 17 00:00:00 2001 From: Danny Diekroeger Date: Wed, 26 Apr 2023 13:33:08 -0700 Subject: [PATCH 1/5] Lightning Boost --- package-lock.json | 272 +--------------------- package.json | 2 +- src/components/modals/send-modal/index.js | 50 +++- src/utils/psbt.js | 48 +++- 4 files changed, 95 insertions(+), 277 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2f206749..c2d680fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "axios": "^1.2.6", "bip32": "^3.1.0", "bitcoin-address-validation": "^2.2.1", - "bitcoinjs-lib": "^6.1.0", + "bitcoinjs-lib": "github:deezy-inc/bitcoinjs-lib", "clsx": "^1.2.1", "ecpair": "^2.1.0", "ethers": "^5.7.2", @@ -1491,186 +1491,6 @@ "node": ">= 10" } }, - "node_modules/@next/swc-darwin-x64": { - "version": "13.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.6.tgz", - "integrity": "sha512-/uOky5PaZDoaU99ohjtNcDTJ6ks/gZ5ykTQDvNZDjIoCxFe3+t06bxsTPY6tAO6uEAw5f6vVFX5H5KLwhrkZCA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-freebsd-x64": { - "version": "13.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.6.tgz", - "integrity": "sha512-qaEALZeV7to6weSXk3Br80wtFQ7cFTpos/q+m9XVRFggu+8Ib895XhMWdJBzew6aaOcMvYR6KQ6JmHA2/eMzWw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm-gnueabihf": { - "version": "13.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.6.tgz", - "integrity": "sha512-OybkbC58A1wJ+JrJSOjGDvZzrVEQA4sprJejGqMwiZyLqhr9Eo8FXF0y6HL+m1CPCpPhXEHz/2xKoYsl16kNqw==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.6.tgz", - "integrity": "sha512-yCH+yDr7/4FDuWv6+GiYrPI9kcTAO3y48UmaIbrKy8ZJpi7RehJe3vIBRUmLrLaNDH3rY1rwoHi471NvR5J5NQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.6.tgz", - "integrity": "sha512-ECagB8LGX25P9Mrmlc7Q/TQBb9rGScxHbv/kLqqIWs2fIXy6Y/EiBBiM72NTwuXUFCNrWR4sjUPSooVBJJ3ESQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.6.tgz", - "integrity": "sha512-GT5w2mruk90V/I5g6ScuueE7fqj/d8Bui2qxdw6lFxmuTgMeol5rnzAv4uAoVQgClOUO/MULilzlODg9Ib3Y4Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "13.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.6.tgz", - "integrity": "sha512-keFD6KvwOPzmat4TCnlnuxJCQepPN+8j3Nw876FtULxo8005Y9Ghcl7ACcR8GoiKoddAq8gxNBrpjoxjQRHeAQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.6.tgz", - "integrity": "sha512-OwertslIiGQluFvHyRDzBCIB07qJjqabAmINlXUYt7/sY7Q7QPE8xVi5beBxX/rxTGPIbtyIe3faBE6Z2KywhQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.6.tgz", - "integrity": "sha512-g8zowiuP8FxUR9zslPmlju7qYbs2XBtTLVSxVikPtUDQedhcls39uKYLvOOd1JZg0ehyhopobRoH1q+MHlIN/w==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-android-arm-eabi": { - "version": "13.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.6.tgz", - "integrity": "sha512-F3/6Z8LH/pGlPzR1AcjPFxx35mPqjE5xZcf+IL+KgbW9tMkp7CYi1y7qKrEWU7W4AumxX/8OINnDQWLiwLasLQ==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-android-arm64": { - "version": "13.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.1.6.tgz", - "integrity": "sha512-cMwQjnB8vrYkWyK/H0Rf2c2pKIH4RGjpKUDvbjVAit6SbwPDpmaijLio0LWFV3/tOnY6kvzbL62lndVA0mkYpw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "13.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.6.tgz", - "integrity": "sha512-KKRQH4DDE4kONXCvFMNBZGDb499Hs+xcFAwvj+rfSUssIDrZOlyfJNy55rH5t2Qxed1e4K80KEJgsxKQN1/fyw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@next/swc-darwin-x64": { "version": "13.1.6", "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.6.tgz", @@ -2323,8 +2143,8 @@ }, "node_modules/bitcoinjs-lib": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.0.tgz", - "integrity": "sha512-eupi1FBTJmPuAZdChnzTXLv2HBqFW2AICpzXZQLniP0V9FWWeeUQSMKES6sP8isy/xO0ijDexbgkdEyFVrsuJw==", + "resolved": "git+ssh://git@github.com/deezy-inc/bitcoinjs-lib.git#73a310e4af361041855ad883edad3168153931bc", + "license": "MIT", "dependencies": { "bech32": "^2.0.0", "bip174": "^2.1.0", @@ -6076,81 +5896,8 @@ "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true } }, "dependencies": { @@ -7094,8 +6841,8 @@ }, "@next/swc-darwin-x64": { "version": "13.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.6.tgz", - "integrity": "sha512-/uOky5PaZDoaU99ohjtNcDTJ6ks/gZ5ykTQDvNZDjIoCxFe3+t06bxsTPY6tAO6uEAw5f6vVFX5H5KLwhrkZCA==", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.6.tgz", + "integrity": "sha512-Ls2OL9hi3YlJKGNdKv8k3X/lLgc3VmLG3a/DeTkAd+lAituJp8ZHmRmm9f9SL84fT3CotlzcgbdaCDfFwFA6bA==", "optional": true }, "@next/swc-freebsd-x64": { @@ -7538,9 +7285,8 @@ } }, "bitcoinjs-lib": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.0.tgz", - "integrity": "sha512-eupi1FBTJmPuAZdChnzTXLv2HBqFW2AICpzXZQLniP0V9FWWeeUQSMKES6sP8isy/xO0ijDexbgkdEyFVrsuJw==", + "version": "git+ssh://git@github.com/deezy-inc/bitcoinjs-lib.git#73a310e4af361041855ad883edad3168153931bc", + "from": "bitcoinjs-lib@github:deezy-inc/bitcoinjs-lib", "requires": { "bech32": "^2.0.0", "bip174": "^2.1.0", diff --git a/package.json b/package.json index 7a5753ef..5c251e75 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "axios": "^1.2.6", "bip32": "^3.1.0", "bitcoin-address-validation": "^2.2.1", - "bitcoinjs-lib": "^6.1.0", + "bitcoinjs-lib": "github:deezy-inc/bitcoinjs-lib", "clsx": "^1.2.1", "ecpair": "^2.1.0", "ethers": "^5.7.2", diff --git a/src/components/modals/send-modal/index.js b/src/components/modals/send-modal/index.js index ef7cf7c9..5bf3765a 100644 --- a/src/components/modals/send-modal/index.js +++ b/src/components/modals/send-modal/index.js @@ -8,8 +8,9 @@ import InputGroup from "react-bootstrap/InputGroup"; import Form from "react-bootstrap/Form"; import { TESTNET, DEFAULT_FEE_RATE } from "@lib/constants"; import { shortenStr, outputValue } from "@utils/crypto"; -import { signAndBroadcastUtxo } from "@utils/psbt"; +import { createAndSignPsbtForBoost, signAndBroadcastUtxo } from "@utils/psbt"; import SessionStorage, { SessionsStorageKeys } from "@services/session-storage"; +import axios from "axios"; import * as bitcoin from "bitcoinjs-lib"; import * as ecc from "tiny-secp256k1"; @@ -18,6 +19,7 @@ import { TailSpin } from "react-loading-icons"; import { InscriptionPreview } from "@components/inscription-preview"; bitcoin.initEccLib(ecc); +const MIN_OUTPUT_VALUE = 600 const SendModal = ({ show, handleModal, utxo, onSale }) => { const [isBtcInputAddressValid, setIsBtcInputAddressValid] = useState(true); @@ -34,7 +36,37 @@ const SendModal = ({ show, handleModal, utxo, onSale }) => { } }, []); - async function sendUtxo() { + async function sendUtxo(boost = false) { + if (boost) { + try { + if (!window.webln) { + alert('Oops looks like you don\'t have a WebLN compatible browser-extension wallet to make the lightning payment. Try getting Alby from getalby.com') + return + } + if (!window.webln.enabled) await window.webln.enable() + + const signedTxHex = await createAndSignPsbtForBoost({ + pubKey: nostrPublicKey, + utxo, + destinationBtcAddress + }) + const { data } = await axios.post(`https://api${TESTNET ? '-testnet': ''}.deezy.io/v1/boost`, { + psbt: signedTxHex, + fee_rate: sendFeeRate + }) + console.log(data) + const result = await window.webln.sendPayment(data.bolt11_invoice) + console.log(result) + } catch (err) { + console.error(err) + alert('something went wrong') + return + } + + toast.success(`Payment sent!`); + handleModal(); + return; + } try { const txId = await signAndBroadcastUtxo({ pubKey: nostrPublicKey, @@ -74,9 +106,11 @@ const SendModal = ({ show, handleModal, utxo, onSale }) => { const feeRateOnChange = (evt) => setSendFeeRate(evt.target.value); + const boostRequired = !!utxo && !!sendFeeRate && outputValue(utxo, sendFeeRate) < MIN_OUTPUT_VALUE + const submit = async () => { setIsSending(true); - await sendUtxo().catch((err) => { + await sendUtxo(boostRequired).catch((err) => { console.error(err); alert(err); return false; @@ -147,9 +181,17 @@ const SendModal = ({ show, handleModal, utxo, onSale }) => {
{!!destinationBtcAddress && {shortenStr(destinationBtcAddress)}} {sendFeeRate} sat/vbyte - {utxo && sendFeeRate && outputValue(utxo, sendFeeRate)} sats + {boostRequired ? 10000 : utxo && sendFeeRate && outputValue(utxo, sendFeeRate)} sats
+ { + boostRequired ? + + Sending will require a small lightning payment to boost the utxo value

+
+ : + <> + }
diff --git a/src/utils/psbt.js b/src/utils/psbt.js index 305f1259..ebd4d23c 100644 --- a/src/utils/psbt.js +++ b/src/utils/psbt.js @@ -42,11 +42,12 @@ async function signTaproot(psbt, inputParams) { return psbt; } -async function signNostr(psbt, inputParams, inputAddressInfo, utxo) { +async function signNostr(psbt, inputParams, inputAddressInfo, utxo, sighashType = bitcoin.Transaction.SIGHASH_DEFAULT) { const publicKey = Buffer.from(await window.nostr.getPublicKey(), "hex"); const input = { ...inputParams, tapInternalKey: toXOnly(publicKey), + // assuming sighashType is already on the inputParams }; psbt.addInput(input); @@ -55,29 +56,29 @@ async function signNostr(psbt, inputParams, inputAddressInfo, utxo) { 0, [inputAddressInfo.output], [utxo.value], - bitcoin.Transaction.SIGHASH_DEFAULT + sighashType ); const sig = await window.nostr.signSchnorr(sigHash.toString("hex")); psbt.updateInput(0, { - tapKeySig: serializeTaprootSignature(Buffer.from(sig, "hex")), + tapKeySig: serializeTaprootSignature(Buffer.from(sig, "hex"), sighashType), }); return psbt; } -async function getSignedPsbt({ psbt, inputParams, inputAddressInfo, utxo }) { +async function getSignedPsbt({ psbt, inputParams, inputAddressInfo, utxo, sighashType = bitcoin.Transaction.SIGHASH_DEFAULT }) { const metamaskDomain = SessionStorage.get(SessionsStorageKeys.DOMAIN); if (metamaskDomain) { return signTaproot(psbt, inputParams); } - return signNostr(psbt, inputParams, inputAddressInfo, utxo); + return signNostr(psbt, inputParams, inputAddressInfo, utxo, sighashType); } -function getInputParams({ utxo, inputAddressInfo }) { - return { +function getInputParams({ utxo, inputAddressInfo, sighashType }) { + const params = { hash: utxo.txid, index: utxo.vout, witnessUtxo: { @@ -85,7 +86,11 @@ function getInputParams({ utxo, inputAddressInfo }) { script: inputAddressInfo.output, }, tapInternalKey: "", - }; + } + if (sighashType) { + params.sighashType = sighashType; + } + return params } function createPsbt({ utxo, inputAddressInfo, destinationBtcAddress, sendFeeRate }) { @@ -102,6 +107,21 @@ function createPsbt({ utxo, inputAddressInfo, destinationBtcAddress, sendFeeRate return { psbt, inputParams }; } +function createPsbtForBoost({ utxo, inputAddressInfo, destinationBtcAddress }) { + const network = TESTNET ? bitcoin.networks.testnet : bitcoin.networks.bitcoin; + + const psbt = new bitcoin.Psbt({ network }); + const sighashType = bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY + const inputParams = getInputParams({ utxo, inputAddressInfo, sighashType }); + const output = 10000 // TODO: make this a constant. + psbt.addOutput({ + address: destinationBtcAddress, + value: output, + }); + + return { psbt, inputParams }; +} + async function broadcastPsbt(psbt) { const tx = psbt.extractTransaction(); const hex = tx.toBuffer().toString("hex"); @@ -112,7 +132,7 @@ async function broadcastPsbt(psbt) { } export async function signAndBroadcastUtxo({ pubKey, utxo, destinationBtcAddress, sendFeeRate }) { - const inputAddressInfo = await getAddressInfo(pubKey); + const inputAddressInfo = getAddressInfo(pubKey); const { psbt, inputParams } = createPsbt({ utxo, inputAddressInfo, destinationBtcAddress, sendFeeRate }); const signed = await getSignedPsbt({ psbt, inputParams, inputAddressInfo, utxo }); // Finalize the PSBT. Note that the transaction will not be broadcast to the Bitcoin network yet. @@ -120,3 +140,13 @@ export async function signAndBroadcastUtxo({ pubKey, utxo, destinationBtcAddress // Send it! return broadcastPsbt(signed); } + +export async function createAndSignPsbtForBoost({ pubKey, utxo, destinationBtcAddress }) { + const inputAddressInfo = getAddressInfo(pubKey); + const { psbt, inputParams } = createPsbtForBoost({ utxo, inputAddressInfo, destinationBtcAddress }); + const sighashType = bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY; + const signed = await getSignedPsbt({ psbt, inputParams, inputAddressInfo, utxo, sighashType }); + // Finalize the PSBT. Note that the transaction will not be broadcast to the Bitcoin network yet. + signed.finalizeAllInputs(); + return signed.toHex() +} From 0bfc0140181645a4ce51d1dced31bb30cde1c71d Mon Sep 17 00:00:00 2001 From: Danny Diekroeger Date: Wed, 26 Apr 2023 13:38:36 -0700 Subject: [PATCH 2/5] formatting --- src/components/modals/send-modal/index.js | 55 ++++++++++++----------- src/utils/psbt.js | 25 ++++++----- 2 files changed, 43 insertions(+), 37 deletions(-) diff --git a/src/components/modals/send-modal/index.js b/src/components/modals/send-modal/index.js index 5bf3765a..663bc349 100644 --- a/src/components/modals/send-modal/index.js +++ b/src/components/modals/send-modal/index.js @@ -19,7 +19,7 @@ import { TailSpin } from "react-loading-icons"; import { InscriptionPreview } from "@components/inscription-preview"; bitcoin.initEccLib(ecc); -const MIN_OUTPUT_VALUE = 600 +const MIN_OUTPUT_VALUE = 600; const SendModal = ({ show, handleModal, utxo, onSale }) => { const [isBtcInputAddressValid, setIsBtcInputAddressValid] = useState(true); @@ -40,27 +40,29 @@ const SendModal = ({ show, handleModal, utxo, onSale }) => { if (boost) { try { if (!window.webln) { - alert('Oops looks like you don\'t have a WebLN compatible browser-extension wallet to make the lightning payment. Try getting Alby from getalby.com') - return + alert( + "Oops looks like you don't have a WebLN compatible browser-extension wallet to make the lightning payment. Try getting Alby from getalby.com" + ); + return; } - if (!window.webln.enabled) await window.webln.enable() + if (!window.webln.enabled) await window.webln.enable(); const signedTxHex = await createAndSignPsbtForBoost({ pubKey: nostrPublicKey, utxo, - destinationBtcAddress - }) - const { data } = await axios.post(`https://api${TESTNET ? '-testnet': ''}.deezy.io/v1/boost`, { + destinationBtcAddress, + }); + const { data } = await axios.post(`https://api${TESTNET ? "-testnet" : ""}.deezy.io/v1/boost`, { psbt: signedTxHex, - fee_rate: sendFeeRate - }) - console.log(data) - const result = await window.webln.sendPayment(data.bolt11_invoice) - console.log(result) + fee_rate: sendFeeRate, + }); + console.log(data); + const result = await window.webln.sendPayment(data.bolt11_invoice); + console.log(result); } catch (err) { - console.error(err) - alert('something went wrong') - return + console.error(err); + alert("something went wrong"); + return; } toast.success(`Payment sent!`); @@ -106,7 +108,7 @@ const SendModal = ({ show, handleModal, utxo, onSale }) => { const feeRateOnChange = (evt) => setSendFeeRate(evt.target.value); - const boostRequired = !!utxo && !!sendFeeRate && outputValue(utxo, sendFeeRate) < MIN_OUTPUT_VALUE + const boostRequired = !!utxo && !!sendFeeRate && outputValue(utxo, sendFeeRate) < MIN_OUTPUT_VALUE; const submit = async () => { setIsSending(true); @@ -181,17 +183,20 @@ const SendModal = ({ show, handleModal, utxo, onSale }) => {
{!!destinationBtcAddress && {shortenStr(destinationBtcAddress)}} {sendFeeRate} sat/vbyte - {boostRequired ? 10000 : utxo && sendFeeRate && outputValue(utxo, sendFeeRate)} sats + + {boostRequired ? 10000 : utxo && sendFeeRate && outputValue(utxo, sendFeeRate)} sats +
- { - boostRequired ? - - Sending will require a small lightning payment to boost the utxo value

-
- : - <> - } + {boostRequired ? ( + + Sending will require a small lightning payment to boost the utxo value +
+
+
+ ) : ( + <> + )}
diff --git a/src/utils/psbt.js b/src/utils/psbt.js index ebd4d23c..b8624255 100644 --- a/src/utils/psbt.js +++ b/src/utils/psbt.js @@ -52,12 +52,7 @@ async function signNostr(psbt, inputParams, inputAddressInfo, utxo, sighashType psbt.addInput(input); - const sigHash = psbt.__CACHE.__TX.hashForWitnessV1( - 0, - [inputAddressInfo.output], - [utxo.value], - sighashType - ); + const sigHash = psbt.__CACHE.__TX.hashForWitnessV1(0, [inputAddressInfo.output], [utxo.value], sighashType); const sig = await window.nostr.signSchnorr(sigHash.toString("hex")); psbt.updateInput(0, { @@ -67,7 +62,13 @@ async function signNostr(psbt, inputParams, inputAddressInfo, utxo, sighashType return psbt; } -async function getSignedPsbt({ psbt, inputParams, inputAddressInfo, utxo, sighashType = bitcoin.Transaction.SIGHASH_DEFAULT }) { +async function getSignedPsbt({ + psbt, + inputParams, + inputAddressInfo, + utxo, + sighashType = bitcoin.Transaction.SIGHASH_DEFAULT, +}) { const metamaskDomain = SessionStorage.get(SessionsStorageKeys.DOMAIN); if (metamaskDomain) { @@ -86,11 +87,11 @@ function getInputParams({ utxo, inputAddressInfo, sighashType }) { script: inputAddressInfo.output, }, tapInternalKey: "", - } + }; if (sighashType) { params.sighashType = sighashType; } - return params + return params; } function createPsbt({ utxo, inputAddressInfo, destinationBtcAddress, sendFeeRate }) { @@ -111,9 +112,9 @@ function createPsbtForBoost({ utxo, inputAddressInfo, destinationBtcAddress }) { const network = TESTNET ? bitcoin.networks.testnet : bitcoin.networks.bitcoin; const psbt = new bitcoin.Psbt({ network }); - const sighashType = bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY + const sighashType = bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY; const inputParams = getInputParams({ utxo, inputAddressInfo, sighashType }); - const output = 10000 // TODO: make this a constant. + const output = 10000; // TODO: make this a constant. psbt.addOutput({ address: destinationBtcAddress, value: output, @@ -148,5 +149,5 @@ export async function createAndSignPsbtForBoost({ pubKey, utxo, destinationBtcAd const signed = await getSignedPsbt({ psbt, inputParams, inputAddressInfo, utxo, sighashType }); // Finalize the PSBT. Note that the transaction will not be broadcast to the Bitcoin network yet. signed.finalizeAllInputs(); - return signed.toHex() + return signed.toHex(); } From ed2a2e9980b4b9e777368ab299e84e6eb4311c63 Mon Sep 17 00:00:00 2001 From: Danny Diekroeger Date: Wed, 26 Apr 2023 13:39:50 -0700 Subject: [PATCH 3/5] hard-code sighash type --- src/utils/psbt.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/psbt.js b/src/utils/psbt.js index b8624255..c2737706 100644 --- a/src/utils/psbt.js +++ b/src/utils/psbt.js @@ -112,7 +112,7 @@ function createPsbtForBoost({ utxo, inputAddressInfo, destinationBtcAddress }) { const network = TESTNET ? bitcoin.networks.testnet : bitcoin.networks.bitcoin; const psbt = new bitcoin.Psbt({ network }); - const sighashType = bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY; + const sighashType = 131 // bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY; const inputParams = getInputParams({ utxo, inputAddressInfo, sighashType }); const output = 10000; // TODO: make this a constant. psbt.addOutput({ @@ -145,7 +145,7 @@ export async function signAndBroadcastUtxo({ pubKey, utxo, destinationBtcAddress export async function createAndSignPsbtForBoost({ pubKey, utxo, destinationBtcAddress }) { const inputAddressInfo = getAddressInfo(pubKey); const { psbt, inputParams } = createPsbtForBoost({ utxo, inputAddressInfo, destinationBtcAddress }); - const sighashType = bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY; + const sighashType = 131 // bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY; const signed = await getSignedPsbt({ psbt, inputParams, inputAddressInfo, utxo, sighashType }); // Finalize the PSBT. Note that the transaction will not be broadcast to the Bitcoin network yet. signed.finalizeAllInputs(); From 7b15a17d556e65e0ea0ef97ff55fac8604d603da Mon Sep 17 00:00:00 2001 From: Danny Diekroeger Date: Wed, 26 Apr 2023 13:40:34 -0700 Subject: [PATCH 4/5] hard-code sighash type --- src/utils/psbt.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/psbt.js b/src/utils/psbt.js index c2737706..488f9cf4 100644 --- a/src/utils/psbt.js +++ b/src/utils/psbt.js @@ -112,7 +112,7 @@ function createPsbtForBoost({ utxo, inputAddressInfo, destinationBtcAddress }) { const network = TESTNET ? bitcoin.networks.testnet : bitcoin.networks.bitcoin; const psbt = new bitcoin.Psbt({ network }); - const sighashType = 131 // bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY; + const sighashType = 131; // bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY; const inputParams = getInputParams({ utxo, inputAddressInfo, sighashType }); const output = 10000; // TODO: make this a constant. psbt.addOutput({ @@ -145,7 +145,7 @@ export async function signAndBroadcastUtxo({ pubKey, utxo, destinationBtcAddress export async function createAndSignPsbtForBoost({ pubKey, utxo, destinationBtcAddress }) { const inputAddressInfo = getAddressInfo(pubKey); const { psbt, inputParams } = createPsbtForBoost({ utxo, inputAddressInfo, destinationBtcAddress }); - const sighashType = 131 // bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY; + const sighashType = 131; // bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY; const signed = await getSignedPsbt({ psbt, inputParams, inputAddressInfo, utxo, sighashType }); // Finalize the PSBT. Note that the transaction will not be broadcast to the Bitcoin network yet. signed.finalizeAllInputs(); From a443d52d168f31fdb1d9920bb548d0afaf228858 Mon Sep 17 00:00:00 2001 From: Danny Diekroeger Date: Wed, 26 Apr 2023 13:45:52 -0700 Subject: [PATCH 5/5] format --- src/components/modals/send-modal/index.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/components/modals/send-modal/index.js b/src/components/modals/send-modal/index.js index 663bc349..57e786f3 100644 --- a/src/components/modals/send-modal/index.js +++ b/src/components/modals/send-modal/index.js @@ -41,7 +41,8 @@ const SendModal = ({ show, handleModal, utxo, onSale }) => { try { if (!window.webln) { alert( - "Oops looks like you don't have a WebLN compatible browser-extension wallet to make the lightning payment. Try getting Alby from getalby.com" + "Oops looks like you don't have a WebLN compatible browser-extension " + + "wallet to make the lightning payment. Try getting Alby from getalby.com" ); return; } @@ -82,11 +83,10 @@ const SendModal = ({ show, handleModal, utxo, onSale }) => { toast.success(`Transaction sent: ${txId}, copied to clipboard`); navigator.clipboard.writeText(txId); handleModal(); - return true; + return; } catch (err) { console.error(err); toast.error(err); - return null; } } @@ -115,7 +115,6 @@ const SendModal = ({ show, handleModal, utxo, onSale }) => { await sendUtxo(boostRequired).catch((err) => { console.error(err); alert(err); - return false; }); // sleep for 1 second to let the tx propagate @@ -188,14 +187,12 @@ const SendModal = ({ show, handleModal, utxo, onSale }) => {
- {boostRequired ? ( + {boostRequired && ( Sending will require a small lightning payment to boost the utxo value

- ) : ( - <> )}