Better lightning address error handling (#2089)

This commit is contained in:
ekzyis 2025-04-10 01:11:52 +02:00 committed by GitHub
parent a7245930c2
commit a3e0b6aa9c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 19 additions and 10 deletions

View File

@ -3,6 +3,7 @@ import { bech32 } from 'bech32'
import { lnAddrSchema } from './validate' import { lnAddrSchema } from './validate'
import { FetchTimeoutError } from '@/lib/fetch' import { FetchTimeoutError } from '@/lib/fetch'
import { WALLET_CREATE_INVOICE_TIMEOUT_MS } from './constants' import { WALLET_CREATE_INVOICE_TIMEOUT_MS } from './constants'
import { assertContentTypeJson, assertResponseOk, ResponseAssertError } from '@/lib/url'
export function encodeLNUrl (url) { export function encodeLNUrl (url) {
const words = bech32.toWords(Buffer.from(url.toString(), 'utf8')) const words = bech32.toWords(Buffer.from(url.toString(), 'utf8'))
@ -35,25 +36,33 @@ export async function lnAddrOptions (addr, { signal } = {}) {
// support HTTP and HTTPS during development // support HTTP and HTTPS during development
protocol = process.env.NEXT_PUBLIC_URL.split('://')[0] protocol = process.env.NEXT_PUBLIC_URL.split('://')[0]
} }
const unexpectedErrorMessage = `An unexpected error occurred fetching the Lightning Address metadata for ${addr}. Check the address and try again.`
let res const unexpectedErrorMessage = 'Lightning address validation failed. Make sure you entered the correct address.'
let body
const url = `${protocol}://${domain}/.well-known/lnurlp/${name}` const url = `${protocol}://${domain}/.well-known/lnurlp/${name}`
try { try {
const req = await fetch(url, { signal }) const res = await fetch(url, { signal })
res = await req.json() assertResponseOk(res)
assertContentTypeJson(res)
body = await res.json()
} catch (err) { } catch (err) {
console.log('Error fetching lnurlp', err) console.log('Error fetching lnurlp:', err)
if (err instanceof ResponseAssertError) {
throw err
}
if (err.name === 'TimeoutError') { if (err.name === 'TimeoutError') {
throw new FetchTimeoutError('GET', url, WALLET_CREATE_INVOICE_TIMEOUT_MS) throw new FetchTimeoutError('GET', url, WALLET_CREATE_INVOICE_TIMEOUT_MS)
} }
// If `fetch` fails, or if `req.json` fails, catch it here and surface a reasonable error if (err.name === 'SyntaxError') {
throw new Error(`GET ${url}: invalid JSON`)
}
throw new Error(unexpectedErrorMessage) throw new Error(unexpectedErrorMessage)
} }
if (res.status === 'ERROR') { if (body.status === 'ERROR') {
// if the response doesn't adhere to spec by providing a `reason` entry, returns a default error message // if the response doesn't adhere to spec by providing a `reason` entry, returns a default error message
throw new Error(res.reason ?? unexpectedErrorMessage) throw new Error(body.reason ?? unexpectedErrorMessage)
} }
const { minSendable, maxSendable, ...leftOver } = res const { minSendable, maxSendable, ...leftOver } = body
return { min: minSendable / 1000, max: maxSendable / 1000, ...leftOver } return { min: minSendable / 1000, max: maxSendable / 1000, ...leftOver }
} }

View File

@ -213,7 +213,7 @@ export function parseNwcUrl (walletConnectUrl) {
return params return params
} }
class ResponseAssertError extends Error { export class ResponseAssertError extends Error {
constructor (res, { method } = {}) { constructor (res, { method } = {}) {
if (method) { if (method) {
super(`${method} ${res.url}: ${res.status} ${res.statusText}`) super(`${method} ${res.url}: ${res.status} ${res.statusText}`)