-
-
Notifications
You must be signed in to change notification settings - Fork 113
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'stackernews:master' into fix_video_loading_safari
- Loading branch information
Showing
54 changed files
with
584 additions
and
372 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { BALANCE_LIMIT_MSATS, PAID_ACTION_TERMINAL_STATES, USER_ID, SN_ADMIN_IDS } from '@/lib/constants' | ||
import { msatsToSats, numWithUnits } from '@/lib/format' | ||
|
||
const MAX_PENDING_PAID_ACTIONS_PER_USER = 100 | ||
const USER_IDS_BALANCE_NO_LIMIT = [...SN_ADMIN_IDS, USER_ID.anon, USER_ID.ad] | ||
|
||
export async function assertBelowMaxPendingInvoices (context) { | ||
const { models, me } = context | ||
const pendingInvoices = await models.invoice.count({ | ||
where: { | ||
userId: me?.id ?? USER_ID.anon, | ||
actionState: { | ||
notIn: PAID_ACTION_TERMINAL_STATES | ||
} | ||
} | ||
}) | ||
|
||
if (pendingInvoices >= MAX_PENDING_PAID_ACTIONS_PER_USER) { | ||
throw new Error('You have too many pending paid actions, cancel some or wait for them to expire') | ||
} | ||
} | ||
|
||
export async function assertBelowBalanceLimit (context) { | ||
const { me, tx } = context | ||
if (!me || USER_IDS_BALANCE_NO_LIMIT.includes(me.id)) return | ||
|
||
// we need to prevent this invoice (and any other pending invoices and withdrawls) | ||
// from causing the user's balance to exceed the balance limit | ||
const pendingInvoices = await tx.invoice.aggregate({ | ||
where: { | ||
userId: me.id, | ||
// p2p invoices are never in state PENDING | ||
actionState: 'PENDING', | ||
actionType: 'RECEIVE' | ||
}, | ||
_sum: { | ||
msatsRequested: true | ||
} | ||
}) | ||
|
||
// Get pending withdrawals total | ||
const pendingWithdrawals = await tx.withdrawl.aggregate({ | ||
where: { | ||
userId: me.id, | ||
status: null | ||
}, | ||
_sum: { | ||
msatsPaying: true, | ||
msatsFeePaying: true | ||
} | ||
}) | ||
|
||
// Calculate total pending amount | ||
const pendingMsats = (pendingInvoices._sum.msatsRequested ?? 0n) + | ||
((pendingWithdrawals._sum.msatsPaying ?? 0n) + (pendingWithdrawals._sum.msatsFeePaying ?? 0n)) | ||
|
||
// Check balance limit | ||
if (pendingMsats + me.msats > BALANCE_LIMIT_MSATS) { | ||
throw new Error( | ||
`pending invoices and withdrawals must not cause balance to exceed ${ | ||
numWithUnits(msatsToSats(BALANCE_LIMIT_MSATS)) | ||
}` | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { PAID_ACTION_PAYMENT_METHODS } from '@/lib/constants' | ||
import { toPositiveBigInt } from '@/lib/validate' | ||
import { notifyDeposit } from '@/lib/webPush' | ||
import { numWithUnits, msatsToSats, satsToMsats } from '@/lib/format' | ||
import { getInvoiceableWallets } from '@/wallets/server' | ||
import { assertBelowBalanceLimit } from './lib/assert' | ||
|
||
export const anonable = false | ||
|
||
export const paymentMethods = [ | ||
PAID_ACTION_PAYMENT_METHODS.P2P, | ||
PAID_ACTION_PAYMENT_METHODS.OPTIMISTIC | ||
] | ||
|
||
export async function getCost ({ msats }) { | ||
return toPositiveBigInt(msats) | ||
} | ||
|
||
export async function getInvoiceablePeer (_, { me, models, cost }) { | ||
if (!me?.proxyReceive) return null | ||
const wallets = await getInvoiceableWallets(me.id, { models }) | ||
|
||
// if the user has any invoiceable wallets and this action will result in their balance | ||
// being greater than their desired threshold | ||
if (wallets.length > 0 && (cost + me.msats) > satsToMsats(me.autoWithdrawThreshold)) { | ||
return me.id | ||
} | ||
|
||
return null | ||
} | ||
|
||
export async function getSybilFeePercent () { | ||
return 10n | ||
} | ||
|
||
export async function perform ({ | ||
invoiceId, | ||
comment, | ||
lud18Data | ||
}, { me, tx }) { | ||
const invoice = await tx.invoice.update({ | ||
where: { id: invoiceId }, | ||
data: { | ||
comment, | ||
lud18Data | ||
}, | ||
include: { invoiceForward: true } | ||
}) | ||
|
||
if (!invoice.invoiceForward) { | ||
// if the invoice is not p2p, assert that the user's balance limit is not exceeded | ||
await assertBelowBalanceLimit({ me, tx }) | ||
} | ||
} | ||
|
||
export async function describe ({ description }, { me, cost, sybilFeePercent }) { | ||
const fee = sybilFeePercent ? cost * BigInt(sybilFeePercent) / 100n : 0n | ||
return description ?? `SN: ${me?.name ?? ''} receives ${numWithUnits(msatsToSats(cost - fee))}` | ||
} | ||
|
||
export async function onPaid ({ invoice }, { tx }) { | ||
if (!invoice) { | ||
throw new Error('invoice is required') | ||
} | ||
|
||
// P2P lnurlp does not need to update the user's balance | ||
if (invoice?.invoiceForward) return | ||
|
||
await tx.user.update({ | ||
where: { id: invoice.userId }, | ||
data: { | ||
msats: { | ||
increment: invoice.msatsReceived | ||
} | ||
} | ||
}) | ||
} | ||
|
||
export async function nonCriticalSideEffects ({ invoice }, { models }) { | ||
await notifyDeposit(invoice.userId, invoice) | ||
await models.$executeRaw` | ||
INSERT INTO pgboss.job (name, data) | ||
VALUES ('nip57', jsonb_build_object('hash', ${invoice.hash}))` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.