diff options
| author | Adam Soltys <[email protected]> | 2024-01-29 15:06:09 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2024-01-29 15:06:09 -0800 |
| commit | 86404448e9a06b6fd5b6ea341bda8ba372f88a66 (patch) | |
| tree | 2d22cfb80d9797236f8f7ec74acc6d98652666a1 | |
| parent | 96f865cd9c9e70205d062e0cda86e0114afb5fbc (diff) | |
| download | coinos-server-86404448e9a06b6fd5b6ea341bda8ba372f88a66.tar.gz coinos-server-86404448e9a06b6fd5b6ea341bda8ba372f88a66.zip | |
autowithdrawals
| -rw-r--r-- | lib/payments.js | 88 | ||||
| -rw-r--r-- | lib/whitelist.js | 2 | ||||
| -rw-r--r-- | routes/payments.js | 13 | ||||
| -rw-r--r-- | routes/users.js | 3 |
4 files changed, 61 insertions, 45 deletions
diff --git a/lib/payments.js b/lib/payments.js index c7cac619..275c6787 100644 --- a/lib/payments.js +++ b/lib/payments.js @@ -107,7 +107,6 @@ 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); @@ -211,19 +210,23 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => { ); } - autowithdraw(user); - console.log("done crediting"); + if (p.confirmed) autowithdraw(user); return p; }; -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 }); +export let autowithdraw = async (user) => { + try { + let { username, autowithdraw, id, destination, threshold, reserve } = user; + if (autowithdraw) { + let to = destination.trim(); + let balance = await g(`balance:${id}`); + let amount = balance - reserve; + console.log("AMT", amount, balance, reserve); + if (balance > threshold) await pay({ amount, to, user }); + } + } catch (e) { + warn(username, "autowithdraw failed", e); } }; @@ -239,13 +242,19 @@ let pay = async ({ amount, to, user }) => { ).toString(); } + let pr; if (lnurl) { let { callback } = await got(lnurl).json(); - let { pr } = await got(`${callback}?amount=${amount}`).json(); - await sendLightning(user, pr, amount); + ({ pr } = await got(`${callback}?amount=${amount}`).json()); + } else if (to.startsWith("ln")) { + try { + await ln.decode(to); + pr = to; + } catch (e) {} } - await sendOnchain({ amount, address: to, user }); + if (pr) await sendLightning({ user, pr, amount }); + else await sendOnchain({ amount, address: to, user, subtract: true }); }; let decode = async (hex) => { @@ -262,8 +271,6 @@ let decode = async (hex) => { } } - console.log("TYPE", type); - return { tx, type }; }; @@ -275,8 +282,6 @@ export let sendOnchain = async (params) => { let node = getNode(type); let { txid } = tx; - console.log("YO"); - if (config[type].walletpass) await node.walletPassphrase(config[type].walletpass, 300); @@ -335,8 +340,6 @@ export let sendOnchain = async (params) => { let amount = total - change; - console.log("HERE", amount, signed.length); - await debit({ hash: txid, amount, @@ -349,9 +352,9 @@ export let sendOnchain = async (params) => { return node.sendRawTransaction(signed); }; -export let sendLightning = async (user, payreq, amount, maxfee, memo) => { +export let sendLightning = async ({ user, pr, amount, maxfee, memo }) => { let p; - let hash = payreq; + let hash = pr; if (typeof amount !== "undefined") { amount = parseInt(amount); @@ -359,21 +362,36 @@ export let sendLightning = async (user, payreq, amount, maxfee, memo) => { } let total = amount; - let { amount_msat, payment_hash } = await ln.decode(payreq); + let { amount_msat, payment_hash } = await ln.decode(pr); if (amount_msat) total = Math.round(amount_msat / 1000); - maxfee = maxfee ? parseInt(maxfee) : Math.round(total * 0.05); + maxfee = + parseInt(maxfee) || + Math.round( + total < 100 + ? total * 5 + : total < 1000 + ? total + : total < 10000 + ? total * 0.5 + : total < 100000 + ? total * 0.1 + : total < 1000000 + ? total * 0.05 + : total * 0.01, + ); + if (maxfee < 0) fail("Max fee cannot be negative"); let invoice = await getInvoice(hash); if (invoice) { if (invoice.uid === user.id) fail("Cannot send to self"); - hash = payreq; + hash = pr; } else { let r; - let { pays } = await ln.listpays(payreq); + let { pays } = await ln.listpays(pr); if (pays.find((p) => p.status === "complete")) fail("Invoice has already been paid"); @@ -381,7 +399,7 @@ export let sendLightning = async (user, payreq, amount, maxfee, memo) => { fail("Payment is already underway"); p = await debit({ - hash: payreq, + hash: pr, amount: total, fee: maxfee, memo, @@ -391,7 +409,7 @@ export let sendLightning = async (user, payreq, amount, maxfee, memo) => { let check = async () => { try { - let { pays } = await ln.listpays(payreq); + let { pays } = await ln.listpays(pr); let recordExists = !!(await g(`payment:${p.id}`)); let paymentFailed = @@ -421,10 +439,10 @@ export let sendLightning = async (user, payreq, amount, maxfee, memo) => { let interval = setInterval(check, 5000); try { - l("paying lightning invoice", payreq.substr(-8), total, amount, maxfee); + l("paying lightning invoice", pr.substr(-8), total, amount, maxfee); r = await ln.pay({ - bolt11: payreq.replace(/\s/g, "").toLowerCase(), + bolt11: pr.replace(/\s/g, "").toLowerCase(), amount_msat: amount_msat ? undefined : amount * 1000, maxfee: maxfee * 1000, retry_for: 5, @@ -462,7 +480,6 @@ export let getNode = (type) => { }; let getAddressType = async (a) => { - console.log("A", a); try { await bc.getAddressInfo(a); return types.bitcoin; @@ -476,16 +493,13 @@ let getAddressType = async (a) => { } }; -export let build = async ({ amount, address, feeRate, user }) => { +export let build = async ({ amount, address, feeRate, user, subtract }) => { let type = await getAddressType(address); - console.log("type", type, amount); let node = getNode(type); amount = parseInt(amount); - let fees = { halfHourFee: 50 }; - - // let fees = await fetch(`${api[type]}/fees/recommended`).then((r) => r.json()); + let fees = await fetch(`${api[type]}/fees/recommended`).then((r) => r.json()); if (!feeRate) { feeRate = fees.halfHourFee; @@ -494,8 +508,6 @@ export let build = async ({ amount, address, feeRate, user }) => { let replaceable = true; let ourfee = Math.round(amount * config.fee); let credit = await g(`credit:${type}:${user.id}`); - let covered = Math.min(credit, ourfee) || 0; - ourfee -= covered; let outs = [{ [address]: btc(amount) }]; @@ -515,7 +527,7 @@ export let build = async ({ amount, address, feeRate, user }) => { let balance = await g(`balance:${user.id}`); - if (amount + fee + ourfee > balance) { + if (subtract || amount + fee + ourfee > balance) { if (amount <= fee + ourfee + dust) fail( `insufficient funds ⚡️${balance} of ⚡️${amount + fee + ourfee + dust}`, diff --git a/lib/whitelist.js b/lib/whitelist.js index 9ef1ceca..fffe779d 100644 --- a/lib/whitelist.js +++ b/lib/whitelist.js @@ -28,6 +28,7 @@ export default [ "profile", "prompt", "pubkey", + "reserve", "salt", "seed", "shopifyStore", @@ -37,5 +38,4 @@ export default [ "twofa", "username", "verified", - "withdrawal", ]; diff --git a/routes/payments.js b/routes/payments.js index 13ef20c2..86362a01 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -7,6 +7,7 @@ import { l, err, warn } from "$lib/logging"; import { bail, fail, getInvoice, btc, sats, SATS } from "$lib/utils"; import { requirePin } from "$lib/auth"; import { + autowithdraw, debit, credit, types, @@ -63,7 +64,7 @@ export default { }) .json(); } else if (payreq) { - p = await sendLightning(user, payreq, amount, maxfee, memo); + p = await sendLightning({ user, pr: payreq, amount, maxfee, memo }); } if (!p) { @@ -270,6 +271,8 @@ export default { .exec(); emit(p.uid, "payment", p); + let user = await g(`user:${p.uid}`); + autowithdraw(user); } } } @@ -285,7 +288,7 @@ export default { try { res.send(await build({ ...body, user })); } catch (e) { - console.log(e) + console.log(e); warn( "problem estimating fee", e.message, @@ -302,11 +305,11 @@ export default { async send({ body, user }, res) { try { await requirePin({ body, user }); - let txid = await sendOnchain({ ...body, user }); + let txid = await sendOnchain({ ...body, user }); res.send({ txid }); } catch (e) { - console.log(e) + console.log(e); warn("payment failed", e.message); res.code(500).send(e.message); } @@ -365,7 +368,7 @@ export default { let r = await got(`${callback}?amount=${amount * 1000}`).json(); if (r.reason) fail(r.rason); let { pr } = r; - let p = await sendLightning(user, pr, amount, maxfee, memo); + let p = await sendLightning({ user, pr, amount, maxfee, memo }); if (!p) { p = await debit({ hash: pr, amount, memo, user }); diff --git a/routes/users.js b/routes/users.js index dff3f7bc..8e2a0d14 100644 --- a/routes/users.js +++ b/routes/users.js @@ -41,6 +41,7 @@ export default { user.prompt = !!user.prompt; if (user.pubkey) user.npub = encode("npub", toWords(Buffer.from(user.pubkey, "hex"))); + res.send(pick(user, whitelist)); } catch (e) { console.log("problem fetching user", e); @@ -237,6 +238,7 @@ export default { "prompt", "profile", "pubkey", + "reserve", "salt", "seed", "threshold", @@ -244,7 +246,6 @@ export default { "twofa", "shopifyToken", "shopifyStore", - "withdrawal", ]; for (let a of attributes) { |
