UNPKG

@getalby/lightning-tools

Version:

Collection of helpful building blocks and tools to develop Bitcoin Lightning web apps

1 lines 127 kB
{"version":3,"file":"lnurl.cjs","sources":["../../node_modules/@noble/hashes/esm/_assert.js","../../node_modules/@noble/hashes/esm/utils.js","../../node_modules/@noble/hashes/esm/_sha2.js","../../node_modules/@noble/hashes/esm/sha256.js","../../src/lnurl/utils.ts","../../node_modules/@scure/base/lib/index.js","../../node_modules/light-bolt11-decoder/bolt11.js","../../src/bolt11/utils.ts","../../src/bolt11/Invoice.ts","../../src/podcasting2/boostagrams.ts","../../src/lnurl/LightningAddress.ts"],"sourcesContent":["function number(n) {\n if (!Number.isSafeInteger(n) || n < 0)\n throw new Error(`Wrong positive integer: ${n}`);\n}\nfunction bool(b) {\n if (typeof b !== 'boolean')\n throw new Error(`Expected boolean, not ${b}`);\n}\nfunction bytes(b, ...lengths) {\n if (!(b instanceof Uint8Array))\n throw new Error('Expected Uint8Array');\n if (lengths.length > 0 && !lengths.includes(b.length))\n throw new Error(`Expected Uint8Array of length ${lengths}, not of length=${b.length}`);\n}\nfunction hash(hash) {\n if (typeof hash !== 'function' || typeof hash.create !== 'function')\n throw new Error('Hash should be wrapped by utils.wrapConstructor');\n number(hash.outputLen);\n number(hash.blockLen);\n}\nfunction exists(instance, checkFinished = true) {\n if (instance.destroyed)\n throw new Error('Hash instance has been destroyed');\n if (checkFinished && instance.finished)\n throw new Error('Hash#digest() has already been called');\n}\nfunction output(out, instance) {\n bytes(out);\n const min = instance.outputLen;\n if (out.length < min) {\n throw new Error(`digestInto() expects output buffer of length at least ${min}`);\n }\n}\nexport { number, bool, bytes, hash, exists, output };\nconst assert = { number, bool, bytes, hash, exists, output };\nexport default assert;\n//# sourceMappingURL=_assert.js.map","/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */\n// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.\n// node.js versions earlier than v19 don't declare it in global scope.\n// For node.js, package.json#exports field mapping rewrites import\n// from `crypto` to `cryptoNode`, which imports native module.\n// Makes the utils un-importable in browsers without a bundler.\n// Once node.js 18 is deprecated, we can just drop the import.\nimport { crypto } from '@noble/hashes/crypto';\nconst u8a = (a) => a instanceof Uint8Array;\n// Cast array to different type\nexport const u8 = (arr) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);\nexport const u32 = (arr) => new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));\n// Cast array to view\nexport const createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n// The rotate right (circular right shift) operation for uint32\nexport const rotr = (word, shift) => (word << (32 - shift)) | (word >>> shift);\n// big-endian hardware is rare. Just in case someone still decides to run hashes:\n// early-throw an error because we don't support BE yet.\nexport const isLE = new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44;\nif (!isLE)\n throw new Error('Non little-endian hardware is not supported');\nconst hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));\n/**\n * @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'\n */\nexport function bytesToHex(bytes) {\n if (!u8a(bytes))\n throw new Error('Uint8Array expected');\n // pre-caching improves the speed 6x\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += hexes[bytes[i]];\n }\n return hex;\n}\n/**\n * @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n */\nexport function hexToBytes(hex) {\n if (typeof hex !== 'string')\n throw new Error('hex string expected, got ' + typeof hex);\n const len = hex.length;\n if (len % 2)\n throw new Error('padded hex string expected, got unpadded hex of length ' + len);\n const array = new Uint8Array(len / 2);\n for (let i = 0; i < array.length; i++) {\n const j = i * 2;\n const hexByte = hex.slice(j, j + 2);\n const byte = Number.parseInt(hexByte, 16);\n if (Number.isNaN(byte) || byte < 0)\n throw new Error('Invalid byte sequence');\n array[i] = byte;\n }\n return array;\n}\n// There is no setImmediate in browser and setTimeout is slow.\n// call of async fn will return Promise, which will be fullfiled only on\n// next scheduler queue processing step and this is exactly what we need.\nexport const nextTick = async () => { };\n// Returns control to thread each 'tick' ms to avoid blocking\nexport async function asyncLoop(iters, tick, cb) {\n let ts = Date.now();\n for (let i = 0; i < iters; i++) {\n cb(i);\n // Date.now() is not monotonic, so in case if clock goes backwards we return return control too\n const diff = Date.now() - ts;\n if (diff >= 0 && diff < tick)\n continue;\n await nextTick();\n ts += diff;\n }\n}\n/**\n * @example utf8ToBytes('abc') // new Uint8Array([97, 98, 99])\n */\nexport function utf8ToBytes(str) {\n if (typeof str !== 'string')\n throw new Error(`utf8ToBytes expected string, got ${typeof str}`);\n return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809\n}\n/**\n * Normalizes (non-hex) string or Uint8Array to Uint8Array.\n * Warning: when Uint8Array is passed, it would NOT get copied.\n * Keep in mind for future mutable operations.\n */\nexport function toBytes(data) {\n if (typeof data === 'string')\n data = utf8ToBytes(data);\n if (!u8a(data))\n throw new Error(`expected Uint8Array, got ${typeof data}`);\n return data;\n}\n/**\n * Copies several Uint8Arrays into one.\n */\nexport function concatBytes(...arrays) {\n const r = new Uint8Array(arrays.reduce((sum, a) => sum + a.length, 0));\n let pad = 0; // walk through each item, ensure they have proper type\n arrays.forEach((a) => {\n if (!u8a(a))\n throw new Error('Uint8Array expected');\n r.set(a, pad);\n pad += a.length;\n });\n return r;\n}\n// For runtime check if class implements interface\nexport class Hash {\n // Safe version that clones internal state\n clone() {\n return this._cloneInto();\n }\n}\nconst toStr = {}.toString;\nexport function checkOpts(defaults, opts) {\n if (opts !== undefined && toStr.call(opts) !== '[object Object]')\n throw new Error('Options should be object or undefined');\n const merged = Object.assign(defaults, opts);\n return merged;\n}\nexport function wrapConstructor(hashCons) {\n const hashC = (msg) => hashCons().update(toBytes(msg)).digest();\n const tmp = hashCons();\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.create = () => hashCons();\n return hashC;\n}\nexport function wrapConstructorWithOpts(hashCons) {\n const hashC = (msg, opts) => hashCons(opts).update(toBytes(msg)).digest();\n const tmp = hashCons({});\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.create = (opts) => hashCons(opts);\n return hashC;\n}\nexport function wrapXOFConstructorWithOpts(hashCons) {\n const hashC = (msg, opts) => hashCons(opts).update(toBytes(msg)).digest();\n const tmp = hashCons({});\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.create = (opts) => hashCons(opts);\n return hashC;\n}\n/**\n * Secure PRNG. Uses `crypto.getRandomValues`, which defers to OS.\n */\nexport function randomBytes(bytesLength = 32) {\n if (crypto && typeof crypto.getRandomValues === 'function') {\n return crypto.getRandomValues(new Uint8Array(bytesLength));\n }\n throw new Error('crypto.getRandomValues must be defined');\n}\n//# sourceMappingURL=utils.js.map","import { exists, output } from './_assert.js';\nimport { Hash, createView, toBytes } from './utils.js';\n// Polyfill for Safari 14\nfunction setBigUint64(view, byteOffset, value, isLE) {\n if (typeof view.setBigUint64 === 'function')\n return view.setBigUint64(byteOffset, value, isLE);\n const _32n = BigInt(32);\n const _u32_max = BigInt(0xffffffff);\n const wh = Number((value >> _32n) & _u32_max);\n const wl = Number(value & _u32_max);\n const h = isLE ? 4 : 0;\n const l = isLE ? 0 : 4;\n view.setUint32(byteOffset + h, wh, isLE);\n view.setUint32(byteOffset + l, wl, isLE);\n}\n// Base SHA2 class (RFC 6234)\nexport class SHA2 extends Hash {\n constructor(blockLen, outputLen, padOffset, isLE) {\n super();\n this.blockLen = blockLen;\n this.outputLen = outputLen;\n this.padOffset = padOffset;\n this.isLE = isLE;\n this.finished = false;\n this.length = 0;\n this.pos = 0;\n this.destroyed = false;\n this.buffer = new Uint8Array(blockLen);\n this.view = createView(this.buffer);\n }\n update(data) {\n exists(this);\n const { view, buffer, blockLen } = this;\n data = toBytes(data);\n const len = data.length;\n for (let pos = 0; pos < len;) {\n const take = Math.min(blockLen - this.pos, len - pos);\n // Fast path: we have at least one block in input, cast it to view and process\n if (take === blockLen) {\n const dataView = createView(data);\n for (; blockLen <= len - pos; pos += blockLen)\n this.process(dataView, pos);\n continue;\n }\n buffer.set(data.subarray(pos, pos + take), this.pos);\n this.pos += take;\n pos += take;\n if (this.pos === blockLen) {\n this.process(view, 0);\n this.pos = 0;\n }\n }\n this.length += data.length;\n this.roundClean();\n return this;\n }\n digestInto(out) {\n exists(this);\n output(out, this);\n this.finished = true;\n // Padding\n // We can avoid allocation of buffer for padding completely if it\n // was previously not allocated here. But it won't change performance.\n const { buffer, view, blockLen, isLE } = this;\n let { pos } = this;\n // append the bit '1' to the message\n buffer[pos++] = 0b10000000;\n this.buffer.subarray(pos).fill(0);\n // we have less than padOffset left in buffer, so we cannot put length in current block, need process it and pad again\n if (this.padOffset > blockLen - pos) {\n this.process(view, 0);\n pos = 0;\n }\n // Pad until full block byte with zeros\n for (let i = pos; i < blockLen; i++)\n buffer[i] = 0;\n // Note: sha512 requires length to be 128bit integer, but length in JS will overflow before that\n // You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen.\n // So we just write lowest 64 bits of that value.\n setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);\n this.process(view, 0);\n const oview = createView(out);\n const len = this.outputLen;\n // NOTE: we do division by 4 later, which should be fused in single op with modulo by JIT\n if (len % 4)\n throw new Error('_sha2: outputLen should be aligned to 32bit');\n const outLen = len / 4;\n const state = this.get();\n if (outLen > state.length)\n throw new Error('_sha2: outputLen bigger than state');\n for (let i = 0; i < outLen; i++)\n oview.setUint32(4 * i, state[i], isLE);\n }\n digest() {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res;\n }\n _cloneInto(to) {\n to || (to = new this.constructor());\n to.set(...this.get());\n const { blockLen, buffer, length, finished, destroyed, pos } = this;\n to.length = length;\n to.pos = pos;\n to.finished = finished;\n to.destroyed = destroyed;\n if (length % blockLen)\n to.buffer.set(buffer);\n return to;\n }\n}\n//# sourceMappingURL=_sha2.js.map","import { SHA2 } from './_sha2.js';\nimport { rotr, wrapConstructor } from './utils.js';\n// SHA2-256 need to try 2^128 hashes to execute birthday attack.\n// BTC network is doing 2^67 hashes/sec as per early 2023.\n// Choice: a ? b : c\nconst Chi = (a, b, c) => (a & b) ^ (~a & c);\n// Majority function, true if any two inpust is true\nconst Maj = (a, b, c) => (a & b) ^ (a & c) ^ (b & c);\n// Round constants:\n// first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311)\n// prettier-ignore\nconst SHA256_K = /* @__PURE__ */ new Uint32Array([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2\n]);\n// Initial state (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):\n// prettier-ignore\nconst IV = /* @__PURE__ */ new Uint32Array([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19\n]);\n// Temporary buffer, not used to store anything between runs\n// Named this way because it matches specification.\nconst SHA256_W = /* @__PURE__ */ new Uint32Array(64);\nclass SHA256 extends SHA2 {\n constructor() {\n super(64, 32, 8, false);\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n this.A = IV[0] | 0;\n this.B = IV[1] | 0;\n this.C = IV[2] | 0;\n this.D = IV[3] | 0;\n this.E = IV[4] | 0;\n this.F = IV[5] | 0;\n this.G = IV[6] | 0;\n this.H = IV[7] | 0;\n }\n get() {\n const { A, B, C, D, E, F, G, H } = this;\n return [A, B, C, D, E, F, G, H];\n }\n // prettier-ignore\n set(A, B, C, D, E, F, G, H) {\n this.A = A | 0;\n this.B = B | 0;\n this.C = C | 0;\n this.D = D | 0;\n this.E = E | 0;\n this.F = F | 0;\n this.G = G | 0;\n this.H = H | 0;\n }\n process(view, offset) {\n // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4)\n SHA256_W[i] = view.getUint32(offset, false);\n for (let i = 16; i < 64; i++) {\n const W15 = SHA256_W[i - 15];\n const W2 = SHA256_W[i - 2];\n const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);\n const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);\n SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;\n }\n // Compression function main loop, 64 rounds\n let { A, B, C, D, E, F, G, H } = this;\n for (let i = 0; i < 64; i++) {\n const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);\n const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);\n const T2 = (sigma0 + Maj(A, B, C)) | 0;\n H = G;\n G = F;\n F = E;\n E = (D + T1) | 0;\n D = C;\n C = B;\n B = A;\n A = (T1 + T2) | 0;\n }\n // Add the compressed chunk to the current hash value\n A = (A + this.A) | 0;\n B = (B + this.B) | 0;\n C = (C + this.C) | 0;\n D = (D + this.D) | 0;\n E = (E + this.E) | 0;\n F = (F + this.F) | 0;\n G = (G + this.G) | 0;\n H = (H + this.H) | 0;\n this.set(A, B, C, D, E, F, G, H);\n }\n roundClean() {\n SHA256_W.fill(0);\n }\n destroy() {\n this.set(0, 0, 0, 0, 0, 0, 0, 0);\n this.buffer.fill(0);\n }\n}\n// Constants from https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf\nclass SHA224 extends SHA256 {\n constructor() {\n super();\n this.A = 0xc1059ed8 | 0;\n this.B = 0x367cd507 | 0;\n this.C = 0x3070dd17 | 0;\n this.D = 0xf70e5939 | 0;\n this.E = 0xffc00b31 | 0;\n this.F = 0x68581511 | 0;\n this.G = 0x64f98fa7 | 0;\n this.H = 0xbefa4fa4 | 0;\n this.outputLen = 28;\n }\n}\n/**\n * SHA2-256 hash function\n * @param message - data that would be hashed\n */\nexport const sha256 = /* @__PURE__ */ wrapConstructor(() => new SHA256());\nexport const sha224 = /* @__PURE__ */ wrapConstructor(() => new SHA224());\n//# sourceMappingURL=sha256.js.map","import {\n KeySendRawData,\n KeysendResponse,\n Event,\n NostrResponse,\n ZapArgs,\n ZapOptions,\n LUD18ServicePayerData,\n LnUrlPayResponse,\n LnUrlRawData,\n} from \"./types\";\nimport { sha256 } from \"@noble/hashes/sha256\";\nimport { bytesToHex } from \"@noble/hashes/utils\";\n\nconst TAG_KEYSEND = \"keysend\";\n\nexport const parseKeysendResponse = (data: KeySendRawData): KeysendResponse => {\n if (data.tag !== TAG_KEYSEND) throw new Error(\"Invalid keysend params\");\n if (data.status !== \"OK\") throw new Error(\"Keysend status not OK\");\n if (!data.pubkey) throw new Error(\"Pubkey does not exist\");\n\n const destination = data.pubkey;\n let customKey, customValue;\n\n if (data.customData && data.customData[0]) {\n customKey = data.customData[0].customKey;\n customValue = data.customData[0].customValue;\n }\n\n return {\n destination,\n customKey,\n customValue,\n };\n};\n\nexport async function generateZapEvent(\n { satoshi, comment, p, e, relays }: ZapArgs,\n options: ZapOptions = {},\n): Promise<Event> {\n const nostr = options.nostr || globalThis.nostr;\n if (!nostr) {\n throw new Error(\"nostr option or window.nostr is not available\");\n }\n\n const nostrTags = [\n [\"relays\", ...relays],\n [\"amount\", satoshi.toString()],\n ];\n if (p) {\n nostrTags.push([\"p\", p]);\n }\n if (e) {\n nostrTags.push([\"e\", e]);\n }\n\n const pubkey = await nostr.getPublicKey();\n\n const nostrEvent: Event = {\n pubkey,\n created_at: Math.floor(Date.now() / 1000),\n kind: 9734,\n tags: nostrTags,\n content: comment ?? \"\",\n };\n\n nostrEvent.id = getEventHash(nostrEvent);\n return await nostr.signEvent(nostrEvent);\n}\n\nexport function validateEvent(event: Event): boolean {\n if (typeof event.content !== \"string\") return false;\n if (typeof event.created_at !== \"number\") return false;\n // ignore these checks because if the pubkey is not set we add it to the event. same for the ID.\n // if (typeof event.pubkey !== \"string\") return false;\n // if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false;\n\n if (!Array.isArray(event.tags)) return false;\n for (let i = 0; i < event.tags.length; i++) {\n const tag = event.tags[i];\n if (!Array.isArray(tag)) return false;\n for (let j = 0; j < tag.length; j++) {\n if (typeof tag[j] === \"object\") return false;\n }\n }\n\n return true;\n}\n\nexport function serializeEvent(evt: Event): string {\n if (!validateEvent(evt))\n throw new Error(\"can't serialize event with wrong or missing properties\");\n\n return JSON.stringify([\n 0,\n evt.pubkey,\n evt.created_at,\n evt.kind,\n evt.tags,\n evt.content,\n ]);\n}\n\nexport function getEventHash(event: Event): string {\n return bytesToHex(sha256(serializeEvent(event)));\n}\n\nexport function parseNostrResponse(\n nostrData: NostrResponse,\n username: string | undefined,\n) {\n let nostrPubkey: string | undefined;\n let nostrRelays: string[] | undefined;\n if (username && nostrData) {\n nostrPubkey = nostrData.names?.[username];\n nostrRelays = nostrPubkey ? nostrData.relays?.[nostrPubkey] : undefined;\n }\n\n return [nostrData, nostrPubkey, nostrRelays] as const;\n}\n\nconst URL_REGEX =\n /((([A-Za-z]{3,9}:(?:\\/\\/)?)(?:[-;:&=+$,\\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=+$,\\w]+@)[A-Za-z0-9.-]+)((?:\\/[+~%/.\\w-_]*)?\\??(?:[-+=&;%@.\\w_]*)#?(?:[\\w]*))?)/;\n\nexport const isUrl = (url: string | null): url is string => {\n if (!url) return false;\n return URL_REGEX.test(url);\n};\n\nexport const isValidAmount = ({\n amount,\n min,\n max,\n}: {\n amount: number;\n min: number;\n max: number;\n}): boolean => {\n return amount > 0 && amount >= min && amount <= max;\n};\n\nconst TAG_PAY_REQUEST = \"payRequest\";\n\n// From: https://github.com/dolcalmi/lnurl-pay/blob/main/src/request-pay-service-params.ts\nexport const parseLnUrlPayResponse = async (\n data: LnUrlRawData,\n): Promise<LnUrlPayResponse> => {\n if (data.tag !== TAG_PAY_REQUEST)\n throw new Error(\"Invalid pay service params\");\n\n const callback = (data.callback + \"\").trim();\n if (!isUrl(callback)) throw new Error(\"Callback must be a valid url\");\n\n const min = Math.ceil(Number(data.minSendable || 0));\n const max = Math.floor(Number(data.maxSendable));\n if (!(min && max) || min > max) throw new Error(\"Invalid pay service params\");\n\n let metadata: Array<Array<string>>;\n let metadataHash: string;\n try {\n metadata = JSON.parse(data.metadata + \"\");\n metadataHash = bytesToHex(sha256(data.metadata + \"\"));\n } catch {\n metadata = [];\n metadataHash = bytesToHex(sha256(\"[]\"));\n }\n\n let email = \"\";\n let image = \"\";\n let description = \"\";\n let identifier = \"\";\n for (let i = 0; i < metadata.length; i++) {\n const [k, v] = metadata[i];\n switch (k) {\n case \"text/plain\":\n description = v;\n break;\n case \"text/identifier\":\n identifier = v;\n break;\n case \"text/email\":\n email = v;\n break;\n case \"image/png;base64\":\n case \"image/jpeg;base64\":\n image = \"data:\" + k + \",\" + v;\n break;\n }\n }\n const payerData = data.payerData as LUD18ServicePayerData | undefined;\n\n let domain: string | undefined;\n try {\n domain = new URL(callback).hostname;\n } catch {\n // fail silently and let domain remain undefined if callback is not a valid URL\n }\n\n return {\n callback,\n fixed: min === max,\n min,\n max,\n domain,\n metadata,\n metadataHash,\n identifier,\n email,\n description,\n image,\n payerData,\n commentAllowed: Number(data.commentAllowed) || 0,\n rawData: data,\n allowsNostr: data.allowsNostr || false,\n };\n};\n","\"use strict\";\n/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.bytes = exports.stringToBytes = exports.str = exports.bytesToString = exports.hex = exports.utf8 = exports.bech32m = exports.bech32 = exports.base58check = exports.base58xmr = exports.base58xrp = exports.base58flickr = exports.base58 = exports.base64url = exports.base64 = exports.base32crockford = exports.base32hex = exports.base32 = exports.base16 = exports.utils = exports.assertNumber = void 0;\nfunction assertNumber(n) {\n if (!Number.isSafeInteger(n))\n throw new Error(`Wrong integer: ${n}`);\n}\nexports.assertNumber = assertNumber;\nfunction chain(...args) {\n const wrap = (a, b) => (c) => a(b(c));\n const encode = Array.from(args)\n .reverse()\n .reduce((acc, i) => (acc ? wrap(acc, i.encode) : i.encode), undefined);\n const decode = args.reduce((acc, i) => (acc ? wrap(acc, i.decode) : i.decode), undefined);\n return { encode, decode };\n}\nfunction alphabet(alphabet) {\n return {\n encode: (digits) => {\n if (!Array.isArray(digits) || (digits.length && typeof digits[0] !== 'number'))\n throw new Error('alphabet.encode input should be an array of numbers');\n return digits.map((i) => {\n assertNumber(i);\n if (i < 0 || i >= alphabet.length)\n throw new Error(`Digit index outside alphabet: ${i} (alphabet: ${alphabet.length})`);\n return alphabet[i];\n });\n },\n decode: (input) => {\n if (!Array.isArray(input) || (input.length && typeof input[0] !== 'string'))\n throw new Error('alphabet.decode input should be array of strings');\n return input.map((letter) => {\n if (typeof letter !== 'string')\n throw new Error(`alphabet.decode: not string element=${letter}`);\n const index = alphabet.indexOf(letter);\n if (index === -1)\n throw new Error(`Unknown letter: \"${letter}\". Allowed: ${alphabet}`);\n return index;\n });\n },\n };\n}\nfunction join(separator = '') {\n if (typeof separator !== 'string')\n throw new Error('join separator should be string');\n return {\n encode: (from) => {\n if (!Array.isArray(from) || (from.length && typeof from[0] !== 'string'))\n throw new Error('join.encode input should be array of strings');\n for (let i of from)\n if (typeof i !== 'string')\n throw new Error(`join.encode: non-string input=${i}`);\n return from.join(separator);\n },\n decode: (to) => {\n if (typeof to !== 'string')\n throw new Error('join.decode input should be string');\n return to.split(separator);\n },\n };\n}\nfunction padding(bits, chr = '=') {\n assertNumber(bits);\n if (typeof chr !== 'string')\n throw new Error('padding chr should be string');\n return {\n encode(data) {\n if (!Array.isArray(data) || (data.length && typeof data[0] !== 'string'))\n throw new Error('padding.encode input should be array of strings');\n for (let i of data)\n if (typeof i !== 'string')\n throw new Error(`padding.encode: non-string input=${i}`);\n while ((data.length * bits) % 8)\n data.push(chr);\n return data;\n },\n decode(input) {\n if (!Array.isArray(input) || (input.length && typeof input[0] !== 'string'))\n throw new Error('padding.encode input should be array of strings');\n for (let i of input)\n if (typeof i !== 'string')\n throw new Error(`padding.decode: non-string input=${i}`);\n let end = input.length;\n if ((end * bits) % 8)\n throw new Error('Invalid padding: string should have whole number of bytes');\n for (; end > 0 && input[end - 1] === chr; end--) {\n if (!(((end - 1) * bits) % 8))\n throw new Error('Invalid padding: string has too much padding');\n }\n return input.slice(0, end);\n },\n };\n}\nfunction normalize(fn) {\n if (typeof fn !== 'function')\n throw new Error('normalize fn should be function');\n return { encode: (from) => from, decode: (to) => fn(to) };\n}\nfunction convertRadix(data, from, to) {\n if (from < 2)\n throw new Error(`convertRadix: wrong from=${from}, base cannot be less than 2`);\n if (to < 2)\n throw new Error(`convertRadix: wrong to=${to}, base cannot be less than 2`);\n if (!Array.isArray(data))\n throw new Error('convertRadix: data should be array');\n if (!data.length)\n return [];\n let pos = 0;\n const res = [];\n const digits = Array.from(data);\n digits.forEach((d) => {\n assertNumber(d);\n if (d < 0 || d >= from)\n throw new Error(`Wrong integer: ${d}`);\n });\n while (true) {\n let carry = 0;\n let done = true;\n for (let i = pos; i < digits.length; i++) {\n const digit = digits[i];\n const digitBase = from * carry + digit;\n if (!Number.isSafeInteger(digitBase) ||\n (from * carry) / from !== carry ||\n digitBase - digit !== from * carry) {\n throw new Error('convertRadix: carry overflow');\n }\n carry = digitBase % to;\n digits[i] = Math.floor(digitBase / to);\n if (!Number.isSafeInteger(digits[i]) || digits[i] * to + carry !== digitBase)\n throw new Error('convertRadix: carry overflow');\n if (!done)\n continue;\n else if (!digits[i])\n pos = i;\n else\n done = false;\n }\n res.push(carry);\n if (done)\n break;\n }\n for (let i = 0; i < data.length - 1 && data[i] === 0; i++)\n res.push(0);\n return res.reverse();\n}\nconst gcd = (a, b) => (!b ? a : gcd(b, a % b));\nconst radix2carry = (from, to) => from + (to - gcd(from, to));\nfunction convertRadix2(data, from, to, padding) {\n if (!Array.isArray(data))\n throw new Error('convertRadix2: data should be array');\n if (from <= 0 || from > 32)\n throw new Error(`convertRadix2: wrong from=${from}`);\n if (to <= 0 || to > 32)\n throw new Error(`convertRadix2: wrong to=${to}`);\n if (radix2carry(from, to) > 32) {\n throw new Error(`convertRadix2: carry overflow from=${from} to=${to} carryBits=${radix2carry(from, to)}`);\n }\n let carry = 0;\n let pos = 0;\n const mask = 2 ** to - 1;\n const res = [];\n for (const n of data) {\n assertNumber(n);\n if (n >= 2 ** from)\n throw new Error(`convertRadix2: invalid data word=${n} from=${from}`);\n carry = (carry << from) | n;\n if (pos + from > 32)\n throw new Error(`convertRadix2: carry overflow pos=${pos} from=${from}`);\n pos += from;\n for (; pos >= to; pos -= to)\n res.push(((carry >> (pos - to)) & mask) >>> 0);\n carry &= 2 ** pos - 1;\n }\n carry = (carry << (to - pos)) & mask;\n if (!padding && pos >= from)\n throw new Error('Excess padding');\n if (!padding && carry)\n throw new Error(`Non-zero padding: ${carry}`);\n if (padding && pos > 0)\n res.push(carry >>> 0);\n return res;\n}\nfunction radix(num) {\n assertNumber(num);\n return {\n encode: (bytes) => {\n if (!(bytes instanceof Uint8Array))\n throw new Error('radix.encode input should be Uint8Array');\n return convertRadix(Array.from(bytes), 2 ** 8, num);\n },\n decode: (digits) => {\n if (!Array.isArray(digits) || (digits.length && typeof digits[0] !== 'number'))\n throw new Error('radix.decode input should be array of strings');\n return Uint8Array.from(convertRadix(digits, num, 2 ** 8));\n },\n };\n}\nfunction radix2(bits, revPadding = false) {\n assertNumber(bits);\n if (bits <= 0 || bits > 32)\n throw new Error('radix2: bits should be in (0..32]');\n if (radix2carry(8, bits) > 32 || radix2carry(bits, 8) > 32)\n throw new Error('radix2: carry overflow');\n return {\n encode: (bytes) => {\n if (!(bytes instanceof Uint8Array))\n throw new Error('radix2.encode input should be Uint8Array');\n return convertRadix2(Array.from(bytes), 8, bits, !revPadding);\n },\n decode: (digits) => {\n if (!Array.isArray(digits) || (digits.length && typeof digits[0] !== 'number'))\n throw new Error('radix2.decode input should be array of strings');\n return Uint8Array.from(convertRadix2(digits, bits, 8, revPadding));\n },\n };\n}\nfunction unsafeWrapper(fn) {\n if (typeof fn !== 'function')\n throw new Error('unsafeWrapper fn should be function');\n return function (...args) {\n try {\n return fn.apply(null, args);\n }\n catch (e) { }\n };\n}\nfunction checksum(len, fn) {\n assertNumber(len);\n if (typeof fn !== 'function')\n throw new Error('checksum fn should be function');\n return {\n encode(data) {\n if (!(data instanceof Uint8Array))\n throw new Error('checksum.encode: input should be Uint8Array');\n const checksum = fn(data).slice(0, len);\n const res = new Uint8Array(data.length + len);\n res.set(data);\n res.set(checksum, data.length);\n return res;\n },\n decode(data) {\n if (!(data instanceof Uint8Array))\n throw new Error('checksum.decode: input should be Uint8Array');\n const payload = data.slice(0, -len);\n const newChecksum = fn(payload).slice(0, len);\n const oldChecksum = data.slice(-len);\n for (let i = 0; i < len; i++)\n if (newChecksum[i] !== oldChecksum[i])\n throw new Error('Invalid checksum');\n return payload;\n },\n };\n}\nexports.utils = { alphabet, chain, checksum, radix, radix2, join, padding };\nexports.base16 = chain(radix2(4), alphabet('0123456789ABCDEF'), join(''));\nexports.base32 = chain(radix2(5), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), padding(5), join(''));\nexports.base32hex = chain(radix2(5), alphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV'), padding(5), join(''));\nexports.base32crockford = chain(radix2(5), alphabet('0123456789ABCDEFGHJKMNPQRSTVWXYZ'), join(''), normalize((s) => s.toUpperCase().replace(/O/g, '0').replace(/[IL]/g, '1')));\nexports.base64 = chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'), padding(6), join(''));\nexports.base64url = chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'), padding(6), join(''));\nconst genBase58 = (abc) => chain(radix(58), alphabet(abc), join(''));\nexports.base58 = genBase58('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz');\nexports.base58flickr = genBase58('123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ');\nexports.base58xrp = genBase58('rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz');\nconst XMR_BLOCK_LEN = [0, 2, 3, 5, 6, 7, 9, 10, 11];\nexports.base58xmr = {\n encode(data) {\n let res = '';\n for (let i = 0; i < data.length; i += 8) {\n const block = data.subarray(i, i + 8);\n res += exports.base58.encode(block).padStart(XMR_BLOCK_LEN[block.length], '1');\n }\n return res;\n },\n decode(str) {\n let res = [];\n for (let i = 0; i < str.length; i += 11) {\n const slice = str.slice(i, i + 11);\n const blockLen = XMR_BLOCK_LEN.indexOf(slice.length);\n const block = exports.base58.decode(slice);\n for (let j = 0; j < block.length - blockLen; j++) {\n if (block[j] !== 0)\n throw new Error('base58xmr: wrong padding');\n }\n res = res.concat(Array.from(block.slice(block.length - blockLen)));\n }\n return Uint8Array.from(res);\n },\n};\nconst base58check = (sha256) => chain(checksum(4, (data) => sha256(sha256(data))), exports.base58);\nexports.base58check = base58check;\nconst BECH_ALPHABET = chain(alphabet('qpzry9x8gf2tvdw0s3jn54khce6mua7l'), join(''));\nconst POLYMOD_GENERATORS = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];\nfunction bech32Polymod(pre) {\n const b = pre >> 25;\n let chk = (pre & 0x1ffffff) << 5;\n for (let i = 0; i < POLYMOD_GENERATORS.length; i++) {\n if (((b >> i) & 1) === 1)\n chk ^= POLYMOD_GENERATORS[i];\n }\n return chk;\n}\nfunction bechChecksum(prefix, words, encodingConst = 1) {\n const len = prefix.length;\n let chk = 1;\n for (let i = 0; i < len; i++) {\n const c = prefix.charCodeAt(i);\n if (c < 33 || c > 126)\n throw new Error(`Invalid prefix (${prefix})`);\n chk = bech32Polymod(chk) ^ (c >> 5);\n }\n chk = bech32Polymod(chk);\n for (let i = 0; i < len; i++)\n chk = bech32Polymod(chk) ^ (prefix.charCodeAt(i) & 0x1f);\n for (let v of words)\n chk = bech32Polymod(chk) ^ v;\n for (let i = 0; i < 6; i++)\n chk = bech32Polymod(chk);\n chk ^= encodingConst;\n return BECH_ALPHABET.encode(convertRadix2([chk % 2 ** 30], 30, 5, false));\n}\nfunction genBech32(encoding) {\n const ENCODING_CONST = encoding === 'bech32' ? 1 : 0x2bc830a3;\n const _words = radix2(5);\n const fromWords = _words.decode;\n const toWords = _words.encode;\n const fromWordsUnsafe = unsafeWrapper(fromWords);\n function encode(prefix, words, limit = 90) {\n if (typeof prefix !== 'string')\n throw new Error(`bech32.encode prefix should be string, not ${typeof prefix}`);\n if (!Array.isArray(words) || (words.length && typeof words[0] !== 'number'))\n throw new Error(`bech32.encode words should be array of numbers, not ${typeof words}`);\n const actualLength = prefix.length + 7 + words.length;\n if (limit !== false && actualLength > limit)\n throw new TypeError(`Length ${actualLength} exceeds limit ${limit}`);\n prefix = prefix.toLowerCase();\n return `${prefix}1${BECH_ALPHABET.encode(words)}${bechChecksum(prefix, words, ENCODING_CONST)}`;\n }\n function decode(str, limit = 90) {\n if (typeof str !== 'string')\n throw new Error(`bech32.decode input should be string, not ${typeof str}`);\n if (str.length < 8 || (limit !== false && str.length > limit))\n throw new TypeError(`Wrong string length: ${str.length} (${str}). Expected (8..${limit})`);\n const lowered = str.toLowerCase();\n if (str !== lowered && str !== str.toUpperCase())\n throw new Error(`String must be lowercase or uppercase`);\n str = lowered;\n const sepIndex = str.lastIndexOf('1');\n if (sepIndex === 0 || sepIndex === -1)\n throw new Error(`Letter \"1\" must be present between prefix and data only`);\n const prefix = str.slice(0, sepIndex);\n const _words = str.slice(sepIndex + 1);\n if (_words.length < 6)\n throw new Error('Data must be at least 6 characters long');\n const words = BECH_ALPHABET.decode(_words).slice(0, -6);\n const sum = bechChecksum(prefix, words, ENCODING_CONST);\n if (!_words.endsWith(sum))\n throw new Error(`Invalid checksum in ${str}: expected \"${sum}\"`);\n return { prefix, words };\n }\n const decodeUnsafe = unsafeWrapper(decode);\n function decodeToBytes(str) {\n const { prefix, words } = decode(str, false);\n return { prefix, words, bytes: fromWords(words) };\n }\n return { encode, decode, decodeToBytes, decodeUnsafe, fromWords, fromWordsUnsafe, toWords };\n}\nexports.bech32 = genBech32('bech32');\nexports.bech32m = genBech32('bech32m');\nexports.utf8 = {\n encode: (data) => new TextDecoder().decode(data),\n decode: (str) => new TextEncoder().encode(str),\n};\nexports.hex = chain(radix2(4), alphabet('0123456789abcdef'), join(''), normalize((s) => {\n if (typeof s !== 'string' || s.length % 2)\n throw new TypeError(`hex.decode: expected string, got ${typeof s} with length ${s.length}`);\n return s.toLowerCase();\n}));\nconst CODERS = {\n utf8: exports.utf8, hex: exports.hex, base16: exports.base16, base32: exports.base32, base64: exports.base64, base64url: exports.base64url, base58: exports.base58, base58xmr: exports.base58xmr\n};\nconst coderTypeError = `Invalid encoding type. Available types: ${Object.keys(CODERS).join(', ')}`;\nconst bytesToString = (type, bytes) => {\n if (typeof type !== 'string' || !CODERS.hasOwnProperty(type))\n throw new TypeError(coderTypeError);\n if (!(bytes instanceof Uint8Array))\n throw new TypeError('bytesToString() expects Uint8Array');\n return CODERS[type].encode(bytes);\n};\nexports.bytesToString = bytesToString;\nexports.str = exports.bytesToString;\nconst stringToBytes = (type, str) => {\n if (!CODERS.hasOwnProperty(type))\n throw new TypeError(coderTypeError);\n if (typeof str !== 'string')\n throw new TypeError('stringToBytes() expects string');\n return CODERS[type].decode(str);\n};\nexports.stringToBytes = stringToBytes;\nexports.bytes = exports.stringToBytes;\n","const {bech32, hex, utf8} = require('@scure/base')\n\n// defaults for encode; default timestamp is current time at call\nconst DEFAULTNETWORK = {\n // default network is bitcoin\n bech32: 'bc',\n pubKeyHash: 0x00,\n scriptHash: 0x05,\n validWitnessVersions: [0]\n}\nconst TESTNETWORK = {\n bech32: 'tb',\n pubKeyHash: 0x6f,\n scriptHash: 0xc4,\n validWitnessVersions: [0]\n}\nconst SIGNETNETWORK = {\n bech32: 'tbs',\n pubKeyHash: 0x6f,\n scriptHash: 0xc4,\n validWitnessVersions: [0]\n}\nconst REGTESTNETWORK = {\n bech32: 'bcrt',\n pubKeyHash: 0x6f,\n scriptHash: 0xc4,\n validWitnessVersions: [0]\n}\nconst SIMNETWORK = {\n bech32: 'sb',\n pubKeyHash: 0x3f,\n scriptHash: 0x7b,\n validWitnessVersions: [0]\n}\n\nconst FEATUREBIT_ORDER = [\n 'option_data_loss_protect',\n 'initial_routing_sync',\n 'option_upfront_shutdown_script',\n 'gossip_queries',\n 'var_onion_optin',\n 'gossip_queries_ex',\n 'option_static_remotekey',\n 'payment_secret',\n 'basic_mpp',\n 'option_support_large_channel'\n]\n\nconst DIVISORS = {\n m: BigInt(1e3),\n u: BigInt(1e6),\n n: BigInt(1e9),\n p: BigInt(1e12)\n}\n\nconst MAX_MILLISATS = BigInt('2100000000000000000')\n\nconst MILLISATS_PER_BTC = BigInt(1e11)\n\nconst TAGCODES = {\n payment_hash: 1,\n payment_secret: 16,\n description: 13,\n payee: 19,\n description_hash: 23, // commit to longer descriptions (used by lnurl-pay)\n expiry: 6, // default: 3600 (1 hour)\n min_final_cltv_expiry: 24, // default: 9\n fallback_address: 9,\n route_hint: 3, // for extra routing info (private etc.)\n feature_bits: 5,\n metadata: 27\n}\n\n// reverse the keys and values of TAGCODES and insert into TAGNAMES\nconst TAGNAMES = {}\nfor (let i = 0, keys = Object.keys(TAGCODES); i < keys.length; i++) {\n const currentName = keys[i]\n const currentCode = TAGCODES[keys[i]].toString()\n TAGNAMES[currentCode] = currentName\n}\n\nconst TAGPARSERS = {\n 1: words => hex.encode(bech32.fromWordsUnsafe(words)), // 256 bits\n 16: words => hex.encode(bech32.fromWordsUnsafe(words)), // 256 bits\n 13: words => utf8.encode(bech32.fromWordsUnsafe(words)), // string variable length\n 19: words => hex.encode(bech32.fromWordsUnsafe(words)), // 264 bits\n 23: words => hex.encode(bech32.fromWordsUnsafe(words)), // 256 bits\n 27: words => hex.encode(bech32.fromWordsUnsafe(words)), // variable\n 6: wordsToIntBE, // default: 3600 (1 hour)\n 24: wordsToIntBE, // default: 9\n 3: routingInfoParser, // for extra routing info (private etc.)\n 5: featureBitsParser // keep feature bits as array of 5 bit words\n}\n\nfunction getUnknownParser(tagCode) {\n return words => ({\n tagCode: parseInt(tagCode),\n words: bech32.encode('unknown', words, Number.MAX_SAFE_INTEGER)\n })\n}\n\nfunction wordsToIntBE(words) {\n return words.reverse().reduce((total, item, index) => {\n return total + item * Math.pow(32, index)\n }, 0)\n}\n\n// first convert from words to buffer, trimming padding where necessary\n// parse in 51 byte chunks. See encoder for details.\nfunction routingInfoParser(words) {\n const routes = []\n let pubkey,\n shortChannelId,\n feeBaseMSats,\n feeProportionalMillionths,\n cltvExpiryDelta\n let routesBuffer = bech32.fromWordsUnsafe(words)\n while (routesBuffer.length > 0) {\n pubkey = hex.encode(routesBuffer.slice(0, 33)) // 33 bytes\n shortChannelId = hex.encode(routesBuffer.slice(33, 41)) // 8 bytes\n feeBaseMSats = parseInt(hex.encode(routesBuffer.slice(41, 45)), 16) // 4 bytes\n feeProportionalMillionths = parseInt(\n hex.encode(routesBuffer.slice(45, 49)),\n 16\n ) // 4 bytes\n cltvExpiryDelta = parseInt(hex.encode(routesBuffer.slice(49, 51)), 16) // 2 bytes\n\n routesBuffer = routesBuffer.slice(51)\n\n routes.push({\n pubkey,\n short_channel_id: shortChannelId,\n fee_base_msat: feeBaseMSats,\n fee_proportional_millionths: feeProportionalMillionths,\n cltv_expiry_delta: cltvExpiryDelta\n })\n }\n return routes\n}\n\nfunction featureBitsParser(words) {\n const bools = words\n .slice()\n .reverse()\n .map(word => [\n !!(word & 0b1),\n !!(word & 0b10),\n !!(word & 0b100),\n !!(word & 0b1000),\n !!(word & 0b10000)\n ])\n .reduce((finalArr, itemArr) => finalArr.concat(itemArr), [])\n while (bools.length < FEATUREBIT_ORDER.length * 2) {\n bools.push(false)\n }\n\n const featureBits = {}\n\n FEATUREBIT_ORDER.forEach((featureName, index) => {\n let status\n if (bools[index * 2]) {\n status = 'required'\n } else if (bools[index * 2 + 1]) {\n status = 'supported'\n } else {\n status = 'unsupported'\n }\n featureBits[featureName] = status\n })\n\n const extraBits = bools.slice(FEATUREBIT_ORDER.length * 2)\n featureBits.extra_bits = {\n start_bit: FEATUREBIT_ORDER.length * 2,\n bits: extraBits,\n has_required: extraBits.reduce(\n (result, bit, index) =>\n index % 2 !== 0 ? result || false : result || bit,\n false\n )\n }\n\n return featureBits\n}\n\nfunction hrpToMillisat(hrpString, outputString) {\n let divisor, value\n if (hrpString.slice(-1).match(/^[munp]$/)) {\n divisor = hrpString.slice(-1)\n value = hrpString.slice(0, -1)\n } else if (hrpString.slice(-1).match(/^[^munp0-9]$/)) {\n throw new Error('Not a valid multiplier for the amount')\n } else {\n value = hrpString\n }\n\n if (!value.match(/^\\d+$/))\n throw new Error('Not a valid human readable amount')\n\n const valueBN = BigInt(value)\n\n const millisatoshisBN = divisor\n ? (valueBN * MILLISATS_PER_BTC) / DIVISORS[divisor]\n : valueBN * MILLISATS_PER_BTC\n\n if (\n (divisor === 'p' && !(valueBN % BigInt(10) === BigInt(0))) ||\n millisatoshisBN > MAX_MILLISATS\n ) {\n throw new Error('Amount is outside of valid range')\n }\n\n return outputString ? millisatoshisBN.toString() : millisatoshisBN\n}\n\n// decode will only have extra comments that aren't covered in encode comments.\n// also if anything is hard to read I'll comment.\nfunction decode(paymentRequest, network) {\n if (typeof paymentRequest !== 'string')\n throw new Error('Lightning Payment Request must be string')\n if (paymentRequest.slice(0, 2).toLowerCase() !== 'ln')\n throw new Error('Not a proper lightning payment request')\n\n const sections = []\n const decoded = bech32.decode(paymentRequest, Number.MAX_SAFE_INTEGER)\n paymentRequest = paymentRequest.toLowerCase()\n const prefix = decoded.prefix\n let words = decoded.words\n let letters = paymentRequest.slice(prefix.length + 1)\n let sigWords = words.slice(-104)\n words = words.slice(0, -104)\n\n // Without reverse lookups, can't say that the multipier at the end must\n // have a number before it, so instead we parse, and if the second group\n // doesn't have anything, there's a good chance the last letter of the\n // coin type got captured by the third group, so just re-regex without\n // the number.\n let prefixMatches = prefix.match(/^ln(\\S+?)(\\d*)([a-zA-Z]?)$/)\n if (prefixMatches && !prefixMatches[2])\n prefixMatches = prefix.match(/^ln(\\S+)$/)\n if (!prefixMatches) {\n throw new Error('Not a proper lightning payment request')\n }\n\n // \"ln\" section\n sections.push({\n name: 'lightning_network',\n letters: 'ln'\n })\n\n // \"bc\" section\n const bech32Prefix = prefixMatches[1]\n let coinNetwork\n if (!network) {\n switch (bech32Prefix) {\n case DEFAULTNETWORK.bech32:\n coinNetwork = DEFAULTNETWORK\n break\n case TESTNETWORK.bech32:\n coinNetwork = TESTNETWORK\n break\n case SIGNETNETWORK.bech32:\n coinNetwork = SIGNETNETWORK\n break\n case REGTESTNETWORK.bech32:\n coinNetwork = REGTESTNETWORK\n break\n case SIMNETWORK.bech32:\n coinNetwork = SIMNETWORK\n break\n }\n } else {\n if (\n network.bech32 === undefined ||\n network.pubKeyHash === undefined ||\n network.scriptHash === undefined ||\n !Array.isArray(network.validWitnessVersions)\n )\n throw new Error('Invalid network')\n coinNetwork = network\n }\n if (!coinNetwork || coinNetwork.bech32 !== bech32Prefix) {\n throw new Error('Unknown coin bech32 prefix')\n }\n sections.push({\n name: 'coin_network',\n letters: bech32Prefix,\n value: coinNetwork\n })\n\n // amount section\n