diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/payments.js | 188 | ||||
| -rw-r--r-- | lib/whitelist.js | 17 |
2 files changed, 174 insertions, 31 deletions
diff --git a/lib/payments.js b/lib/payments.js index c924dd2b..c7cac619 100644 --- a/lib/payments.js +++ b/lib/payments.js @@ -8,11 +8,6 @@ import { l, err, warn } from "$lib/logging"; import { btc, fail, getInvoice, sleep, wait, SATS, sats } from "$lib/utils"; import { callWebhook } from "$lib/webhooks"; import got from "got"; -import { spawn } from "child_process"; -import ffmpeg from "fluent-ffmpeg"; -import fs from "fs"; -import EventEmitter from "events"; -import crc32 from "buffer-crc32"; import { mqtt1, mqtt2 } from "$lib/mqtt"; import { mail, templates } from "$lib/mail"; @@ -45,7 +40,7 @@ export let debit = async ({ }) => { let ref; let { id: uid, currency, username } = user; - if (!rate) rate = store.rates[currency]; + if (!rate) rate = store.rates[currency]; let invoice = await getInvoice(hash); let iid; @@ -112,6 +107,7 @@ export let debit = async ({ }; export let credit = async (hash, amount, memo, ref, type = types.internal) => { + console.log("crediting"); amount = parseInt(amount) || 0; let inv = await getInvoice(hash); @@ -187,6 +183,7 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => { emit(uid, "payment", p); notify(user, `Received ${amount} sats`); callWebhook(inv, p); + l(username, "received", type, amount); if (user.verified && user.notify) { mail(user, "Payment received", templates.paymentReceived, { @@ -200,20 +197,159 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => { }); } - mqtt1.publish( - username, - `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`, - ); + if (config.mqtt1) { + mqtt1.publish( + username, + `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`, + ); + } - mqtt2.publish( - username, - `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`, - ); + if (config.mqtt2) { + mqtt2.publish( + username, + `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`, + ); + } + + autowithdraw(user); + console.log("done crediting"); return p; }; -export let payLightning = async (user, payreq, amount, maxfee, memo) => { +let autowithdraw = async (user) => { + let { autowithdraw, id, destination, threshold, withdrawal } = user; + destination = destination.trim(); + if (autowithdraw) { + let balance = await g(`balance:${id}`); + if (balance > threshold) + await pay({ amount: withdrawal, to: destination, user }); + } +}; + +let pay = async ({ amount, to, user }) => { + amount = parseInt(amount) || 0; + let lnurl; + if (to.includes("@") && to.includes(".")) { + let [name, domain] = to.split("@"); + lnurl = `https://${domain}/.well-known/lnurlp/${name}`; + } else if (to.startsWith("lnurl")) { + lnurl = Buffer.from( + bech32.fromWords(bech32.decode(text, 20000).words), + ).toString(); + } + + if (lnurl) { + let { callback } = await got(lnurl).json(); + let { pr } = await got(`${callback}?amount=${amount}`).json(); + await sendLightning(user, pr, amount); + } + + await sendOnchain({ amount, address: to, user }); +}; + +let decode = async (hex) => { + let type, tx; + try { + tx = await bc.decodeRawTransaction(hex); + type = types.bitcoin; + } catch (e) { + try { + tx = await lq.decodeRawTransaction(hex); + type = types.liquid; + } catch (e) { + fail("unrecognized tx"); + } + } + + console.log("TYPE", type); + + return { tx, type }; +}; + +export let sendOnchain = async (params) => { + let { hex, rate, user } = params; + if (!hex) ({ hex } = await build(params)); + + let { tx, type } = await decode(hex); + let node = getNode(type); + let { txid } = tx; + + console.log("YO"); + + if (config[type].walletpass) + await node.walletPassphrase(config[type].walletpass, 300); + + let { hex: signed } = await node.signRawTransactionWithWallet( + type === types.liquid ? await node.blindRawTransaction(hex) : hex, + ); + + ({ txid } = await node.decodeRawTransaction(signed)); + + let r = await node.testMempoolAccept([signed]); + if (!r[0].allowed) fail("transaction rejected"); + + let total = 0; + let fee = 0; + let change = 0; + + if (type === types.liquid) { + for (let { + asset, + scriptPubKey: { address, type }, + value, + } of tx.vout) { + if (asset !== config.liquid.btc) fail("only L-BTC supported"); + if (type === "fee") fee = sats(value); + else { + total += sats(value); + + if (address) { + if ((await node.getAddressInfo(address)).ismine) { + change += sats(value); + } + } + } + } + } else { + let totalIn = 0; + for await (let { txid, vout } of tx.vin) { + let hex = await node.getRawTransaction(txid); + let tx = await node.decodeRawTransaction(hex); + totalIn += sats(tx.vout[vout].value); + } + + for (let { + scriptPubKey: { address }, + value, + } of tx.vout) { + total += sats(value); + if (await g(`invoice:${address}`)) + fail("Cannot send to internal address"); + + if ((await node.getAddressInfo(address)).ismine) change += sats(value); + } + + fee = totalIn - total; + } + + let amount = total - change; + + console.log("HERE", amount, signed.length); + + await debit({ + hash: txid, + amount, + fee, + rate, + user, + type, + }); + + return node.sendRawTransaction(signed); +}; + +export let sendLightning = async (user, payreq, amount, maxfee, memo) => { let p; let hash = payreq; @@ -325,25 +461,31 @@ export let getNode = (type) => { else fail("unrecognized transaction type"); }; -export let build = async ({ amount, address, feeRate }, user) => { - let type; - +let getAddressType = async (a) => { + console.log("A", a); try { - await bc.getAddressInfo(address); - type = types.bitcoin; + await bc.getAddressInfo(a); + return types.bitcoin; } catch (e) { try { - await lq.getAddressInfo(address); - type = types.liquid; + await lq.getAddressInfo(a); + return types.liquid; } catch (e) { fail("unrecognized address"); } } +}; + +export let build = async ({ amount, address, feeRate, user }) => { + let type = await getAddressType(address); + console.log("type", type, amount); let node = getNode(type); amount = parseInt(amount); - let fees = await fetch(`${api[type]}/fees/recommended`).then((r) => r.json()); + let fees = { halfHourFee: 50 }; + + // let fees = await fetch(`${api[type]}/fees/recommended`).then((r) => r.json()); if (!feeRate) { feeRate = fees.halfHourFee; diff --git a/lib/whitelist.js b/lib/whitelist.js index 3c99777c..9ef1ceca 100644 --- a/lib/whitelist.js +++ b/lib/whitelist.js @@ -1,10 +1,9 @@ export default [ - "autowithdraw", - "notify", - "anon", - "balance", "address", "admin", + "anon", + "autowithdraw", + "balance", "banner", "cipher", "currencies", @@ -13,28 +12,30 @@ export default [ "display", "email", "fiat", - "follows", "followers", + "follows", "haspin", "hasprinter", "id", "index", + "keys", "language", "linked", - "keys", "locktime", "nip5", + "notify", "payments", "profile", "prompt", "pubkey", "salt", "seed", + "shopifyStore", + "shopifyToken", "theme", "threshold", "twofa", "username", "verified", - "shopifyStore", - "shopifyToken", + "withdrawal", ]; |
