SN wallet transaction details (#550)

* display bolt11 info and preimage for invoices

* Remove preimage attempt for wdrwl, since it doesn't make sense

Other various code cleanup

* Only include preimage for confirmed paid and settled invoices
This commit is contained in:
SatsAllDay 2023-10-20 20:25:22 -04:00 committed by GitHub
parent 72b8b5b634
commit 56111efd6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 76 additions and 11 deletions

View File

@ -1,4 +1,4 @@
import { createHodlInvoice, createInvoice, decodePaymentRequest, payViaPaymentRequest, cancelHodlInvoice } from 'ln-service' import { createHodlInvoice, createInvoice, decodePaymentRequest, payViaPaymentRequest, cancelHodlInvoice, getInvoice as getInvoiceFromLnd } from 'ln-service'
import { GraphQLError } from 'graphql' import { GraphQLError } from 'graphql'
import crypto from 'crypto' import crypto from 'crypto'
import serialize from './serial' import serialize from './serial'
@ -40,6 +40,15 @@ export async function getInvoice (parent, { id }, { me, models, lnd }) {
} catch (err) { } catch (err) {
} }
try {
if (inv.confirmedAt) {
const lnInv = await getInvoiceFromLnd({ id: inv.hash, lnd })
inv.preimage = lnInv.secret
}
} catch (err) {
console.error('error fetching invoice from LND', err)
}
return inv return inv
} }

View File

@ -30,6 +30,7 @@ export default gql`
lud18Data: JSONObject lud18Data: JSONObject
hmac: String hmac: String
isHeld: Boolean isHeld: Boolean
preimage: String
} }
type Withdrawl { type Withdrawl {

49
components/bolt11-info.js Normal file
View File

@ -0,0 +1,49 @@
import { decode } from 'bolt11'
import AccordianItem from './accordian-item'
import { CopyInput } from './form'
export default ({ bolt11, preimage }) => {
const { tagsObject: { description, payment_hash: paymentHash } } = decode(bolt11)
if (!description && !paymentHash && !preimage) {
return null
}
return (
<div className='w-100'>
<AccordianItem
header='BOLT11 information'
body={
<>
{description &&
<CopyInput
label='description'
size='sm'
groupClassName='w-100'
readOnly
noForm
placeholder={description}
/>}
{paymentHash &&
<CopyInput
label='payment hash'
size='sm'
groupClassName='w-100'
readOnly
noForm
placeholder={paymentHash}
/>}
{preimage &&
<CopyInput
label='preimage'
size='sm'
groupClassName='w-100'
readOnly
noForm
placeholder={preimage}
/>}
</>
}
/>
</div>
)
}

View File

@ -4,7 +4,6 @@ import BootstrapForm from 'react-bootstrap/Form'
import { Formik, Form as FormikForm, useFormikContext, useField, FieldArray } from 'formik' import { Formik, Form as FormikForm, useFormikContext, useField, FieldArray } from 'formik'
import React, { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react' import React, { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react'
import copy from 'clipboard-copy' import copy from 'clipboard-copy'
import Thumb from '../svgs/thumb-up-fill.svg'
import Col from 'react-bootstrap/Col' import Col from 'react-bootstrap/Col'
import Dropdown from 'react-bootstrap/Dropdown' import Dropdown from 'react-bootstrap/Dropdown'
import Nav from 'react-bootstrap/Nav' import Nav from 'react-bootstrap/Nav'
@ -53,12 +52,15 @@ export function SubmitButton ({
} }
export function CopyInput (props) { export function CopyInput (props) {
const [copied, setCopied] = useState(false) const toaster = useToast()
const handleClick = () => { const handleClick = async () => {
copy(props.placeholder) try {
setCopied(true) await copy(props.placeholder)
setTimeout(() => setCopied(false), 1500) toaster.success('copied')
} catch (err) {
toaster.danger('failed to copy')
}
} }
return ( return (
@ -69,10 +71,9 @@ export function CopyInput (props) {
className={styles.appendButton} className={styles.appendButton}
size={props.size} size={props.size}
onClick={handleClick} onClick={handleClick}
> >copy
{copied ? <Thumb width={18} height={18} /> : 'copy'}
</Button> </Button>
} }
{...props} {...props}
/> />
) )

View File

@ -12,6 +12,7 @@ import { useShowModal } from './modal'
import { sleep } from '../lib/time' import { sleep } from '../lib/time'
import Countdown from './countdown' import Countdown from './countdown'
import PayerData from './payer-data' import PayerData from './payer-data'
import Bolt11Info from './bolt11-info'
export function Invoice ({ invoice, onPayment, info, successVerb }) { export function Invoice ({ invoice, onPayment, info, successVerb }) {
const [expired, setExpired] = useState(new Date(invoice.expiredAt) <= new Date()) const [expired, setExpired] = useState(new Date(invoice.expiredAt) <= new Date())
@ -39,7 +40,7 @@ export function Invoice ({ invoice, onPayment, info, successVerb }) {
} }
}, [invoice.confirmedAt, invoice.isHeld, invoice.satsReceived]) }, [invoice.confirmedAt, invoice.isHeld, invoice.satsReceived])
const { nostr, comment, lud18Data } = invoice const { nostr, comment, lud18Data, bolt11, preimage } = invoice
return ( return (
<> <>
@ -85,6 +86,7 @@ export function Invoice ({ invoice, onPayment, info, successVerb }) {
body={<span className='text-muted ms-3'>{comment}</span>} body={<span className='text-muted ms-3'>{comment}</span>}
/> />
</div>} </div>}
<Bolt11Info bolt11={bolt11} preimage={preimage} />
</> </>
) )
} }

View File

@ -16,6 +16,7 @@ export const INVOICE = gql`
isHeld isHeld
comment comment
lud18Data lud18Data
preimage
} }
}` }`

View File

@ -8,6 +8,7 @@ import { WITHDRAWL } from '../../fragments/wallet'
import Link from 'next/link' import Link from 'next/link'
import { SSR } from '../../lib/constants' import { SSR } from '../../lib/constants'
import { numWithUnits } from '../../lib/format' import { numWithUnits } from '../../lib/format'
import Bolt11Info from '../../components/bolt11-info'
export default function Withdrawl () { export default function Withdrawl () {
return ( return (
@ -97,6 +98,7 @@ function LoadWithdrawl () {
/> />
</div> </div>
<InvoiceStatus variant={variant} status={status} /> <InvoiceStatus variant={variant} status={status} />
<Bolt11Info bolt11={data.withdrawl.bolt11} />
</> </>
) )
} }