mirror of
https://github.com/coracle-social/coracle.git
synced 2024-10-06 11:43:30 +00:00
remove bolt11, bech32 and js-lnurl dependencies.
replace with much faster invoice amount parsing. fix lnurl encoding/decoding.
This commit is contained in:
parent
17afc35d1c
commit
0fb38749ac
BIN
package-lock.json
generated
BIN
package-lock.json
generated
Binary file not shown.
@ -29,15 +29,13 @@
|
||||
"@bugsnag/js": "^7.18.0",
|
||||
"@fortawesome/fontawesome-free": "^6.2.1",
|
||||
"@noble/secp256k1": "^1.7.0",
|
||||
"@scure/base": "^1.1.1",
|
||||
"@tsconfig/svelte": "^3.0.0",
|
||||
"bech32": "^2.0.0",
|
||||
"bolt11": "^1.4.0",
|
||||
"classnames": "^2.3.2",
|
||||
"compressorjs": "^1.1.1",
|
||||
"fuse.js": "^6.6.2",
|
||||
"hurdak": "github:ConsignCloud/hurdak",
|
||||
"husky": "^8.0.3",
|
||||
"js-lnurl": "^0.5.1",
|
||||
"localforage": "^1.10.0",
|
||||
"localforage-memoryStorageDriver": "^0.9.2",
|
||||
"nostr-tools": "^1.7.4",
|
||||
|
@ -200,4 +200,3 @@ const applyContext = (notes, context) => {
|
||||
export default {
|
||||
listen, load, loadPeople, personKinds, loadParents, streamContext, applyContext,
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
import {uniq, pick, identity, isEmpty} from 'ramda'
|
||||
import {nip05} from 'nostr-tools'
|
||||
import {getParams} from 'js-lnurl'
|
||||
import {noop, createMap, ensurePlural, chunk, switcherFn} from 'hurdak/lib/hurdak'
|
||||
import {log} from 'src/util/logger'
|
||||
import {hexToBech32, tryFetch, now, sleep, tryJson, timedelta, shuffle, hash} from 'src/util/misc'
|
||||
import {lnurlEncode, lnurlDecode, tryFetch, now, sleep, tryJson, timedelta, shuffle, hash} from 'src/util/misc'
|
||||
import {Tags, roomAttrs, personKinds, isRelay, isShareableRelay, normalizeRelayUrl} from 'src/util/nostr'
|
||||
import database from 'src/agent/database'
|
||||
|
||||
@ -308,25 +307,31 @@ const verifyNip05 = (pubkey, as) =>
|
||||
}, noop)
|
||||
|
||||
const verifyZapper = async (pubkey, address) => {
|
||||
// Try to parse it as a lud06 LNURL
|
||||
let zapper = await getParams(address) as any
|
||||
let lnurl = address
|
||||
|
||||
// If that failed, try to parse it as a lud16 address
|
||||
if (zapper.status === 'ERROR' && address.includes('@')) {
|
||||
let url
|
||||
if (address.startsWith('lnurl1')) {
|
||||
// Try to parse it as a lud06 LNURL
|
||||
url = lnurlDecode(address)
|
||||
const lnurl = address
|
||||
} else if (address.includes('@')) {
|
||||
// Otherwise try to parse it as a lud16 address
|
||||
const [name, domain] = address.split('@')
|
||||
|
||||
if (!domain || !name) {
|
||||
return
|
||||
}
|
||||
|
||||
const url = `https://${domain}/.well-known/lnurlp/${name}`
|
||||
const res = await tryFetch(() => fetch(url))
|
||||
url = `https://${domain}/.well-known/lnurlp/${name}`
|
||||
} else {
|
||||
// Otherwise this is not valid
|
||||
return
|
||||
}
|
||||
|
||||
if (res) {
|
||||
zapper = await res.json()
|
||||
lnurl = hexToBech32('lnurl', url)
|
||||
}
|
||||
const res = await tryFetch(() => fetch(url))
|
||||
|
||||
let zapper
|
||||
if (res) {
|
||||
zapper = await res.json()
|
||||
lnurl = lnurlEncode('lnurl', url)
|
||||
}
|
||||
|
||||
if (zapper?.allowsNostr && zapper?.nostrPubkey) {
|
||||
|
46
src/util/lightning.ts
Normal file
46
src/util/lightning.ts
Normal file
@ -0,0 +1,46 @@
|
||||
const DIVISORS = {
|
||||
m: BigInt(1e3),
|
||||
u: BigInt(1e6),
|
||||
n: BigInt(1e9),
|
||||
p: BigInt(1e12)
|
||||
}
|
||||
|
||||
const MAX_MILLISATS = BigInt('2100000000000000000')
|
||||
|
||||
const MILLISATS_PER_BTC = BigInt(1e11)
|
||||
|
||||
function hrpToMillisat(hrpString: string) {
|
||||
let divisor, value
|
||||
if (hrpString.slice(-1).match(/^[munp]$/)) {
|
||||
divisor = hrpString.slice(-1)
|
||||
value = hrpString.slice(0, -1)
|
||||
} else if (hrpString.slice(-1).match(/^[^munp0-9]$/)) {
|
||||
throw new Error('Not a valid multiplier for the amount')
|
||||
} else {
|
||||
value = hrpString
|
||||
}
|
||||
|
||||
if (!value.match(/^\d+$/))
|
||||
throw new Error('Not a valid human readable amount')
|
||||
|
||||
const valueBN = BigInt(value)
|
||||
|
||||
const millisatoshisBN = divisor
|
||||
? (valueBN * MILLISATS_PER_BTC) / DIVISORS[divisor]
|
||||
: valueBN * MILLISATS_PER_BTC
|
||||
|
||||
if (
|
||||
(divisor === 'p' && !(valueBN % BigInt(10) === BigInt(0))) ||
|
||||
millisatoshisBN > MAX_MILLISATS
|
||||
) {
|
||||
throw new Error('Amount is outside of valid range')
|
||||
}
|
||||
|
||||
return millisatoshisBN
|
||||
}
|
||||
|
||||
export function invoiceAmount(bolt11: string): number {
|
||||
const hrp = bolt11.match(/lnbc(\d+\w)/)
|
||||
const bn = hrpToMillisat(hrp[1])
|
||||
return Number(bn)
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
import {bech32} from 'bech32'
|
||||
import {Buffer} from 'buffer'
|
||||
import {bech32, utf8} from '@scure/base'
|
||||
import {debounce, throttle} from 'throttle-debounce'
|
||||
import {aperture, path as getPath, allPass, pipe, isNil, complement, equals, is, pluck, sum, identity, sortBy} from "ramda"
|
||||
import Fuse from "fuse.js/dist/fuse.min.js"
|
||||
@ -340,11 +339,11 @@ export const uploadFile = (url, fileObj) => {
|
||||
return fetchJson(url, {method: 'POST', body})
|
||||
}
|
||||
|
||||
export const hexToBech32 = (prefix, hex) =>
|
||||
bech32.encode(prefix, bech32.toWords(Buffer.from(hex, 'hex')))
|
||||
export const lnurlEncode = (prefix, url) =>
|
||||
bech32.encode(prefix, bech32.toWords(utf8.decode(url)))
|
||||
|
||||
export const bech32ToHex = b32 =>
|
||||
Buffer.from(bech32.fromWords(bech32.decode(b32).words)).toString('hex')
|
||||
export const lnurlDecode = b32 =>
|
||||
utf8.encode(bech32.fromWords(bech32.decode(b32).words))
|
||||
|
||||
export const formatSats = sats => {
|
||||
const formatter = new Intl.NumberFormat()
|
||||
|
@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import cx from 'classnames'
|
||||
import bolt11 from 'bolt11'
|
||||
import QRCode from 'qrcode'
|
||||
import {nip19} from 'nostr-tools'
|
||||
import {find, sum, last, whereEq, without, uniq, pluck, reject, propEq} from 'ramda'
|
||||
@ -9,9 +8,10 @@
|
||||
import {slide} from 'svelte/transition'
|
||||
import {navigate} from 'svelte-routing'
|
||||
import {quantify} from 'hurdak/lib/hurdak'
|
||||
import {Tags, findRootId, findReplyId, displayPerson, isLike} from "src/util/nostr"
|
||||
import {Tags, findRootId, findReplyId, displayPerson, isLike} from 'src/util/nostr'
|
||||
import {formatTimestamp, now, tryJson, stringToColor, formatSats, fetchJson} from 'src/util/misc'
|
||||
import {extractUrls, copyToClipboard} from "src/util/html"
|
||||
import {invoiceAmount} from 'src/util/lightning'
|
||||
import ImageCircle from 'src/partials/ImageCircle.svelte'
|
||||
import Input from 'src/partials/Input.svelte'
|
||||
import Textarea from 'src/partials/Textarea.svelte'
|
||||
@ -74,7 +74,7 @@
|
||||
|
||||
return tryJson(() => ({
|
||||
...zap,
|
||||
invoice: bolt11.decode(zapMeta.bolt11),
|
||||
invoiceAmount: invoiceAmount(zapMeta.bolt11),
|
||||
request: JSON.parse(zapMeta.description),
|
||||
}))
|
||||
})
|
||||
@ -88,11 +88,11 @@
|
||||
return false
|
||||
}
|
||||
|
||||
const {invoice, request} = zap
|
||||
const {invoiceAmount, request} = zap
|
||||
const reqMeta = Tags.from(request).asMeta()
|
||||
|
||||
// Verify that the zapper actually sent the requested amount (if it was supplied)
|
||||
if (reqMeta.amount && reqMeta.amount !== parseInt(invoice.millisatoshis)) {
|
||||
if (reqMeta.amount && parseInt(reqMeta.amount) !== invoiceAmount) {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -114,7 +114,7 @@
|
||||
$: zapped = find(z => z.request.pubkey === $profile?.pubkey, zaps)
|
||||
$: $likesCount = likes.length
|
||||
$: $flagsCount = flags.length
|
||||
$: $zapsTotal = sum(zaps.map(zap => zap.invoice.satoshis))
|
||||
$: $zapsTotal = sum(zaps.map(zap => zap.invoiceAmount)) / 1000
|
||||
$: $repliesCount = note.replies.length
|
||||
$: visibleNotes = note.replies.filter(r => showContext ? true : !r.isContext)
|
||||
$: canZap = $person?.zapper && user.canZap()
|
||||
|
Loading…
Reference in New Issue
Block a user