first commit
Made-with: Cursor
This commit is contained in:
69
backend/src/services/quote.ts
Normal file
69
backend/src/services/quote.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { randomInt } from "crypto";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { config } from "../config.js";
|
||||
import { getDb } from "../db/index.js";
|
||||
import { getWalletBalanceSats } from "./lnbits.js";
|
||||
|
||||
const QUOTE_TTL_SECONDS = 60;
|
||||
|
||||
interface PayoutBucket {
|
||||
sats: number;
|
||||
weight: number;
|
||||
}
|
||||
|
||||
function getPayoutBuckets(): PayoutBucket[] {
|
||||
return [
|
||||
{ sats: config.payoutSmallSats, weight: config.payoutWeightSmall },
|
||||
{ sats: config.payoutMediumSats, weight: config.payoutWeightMedium },
|
||||
{ sats: config.payoutLargeSats, weight: config.payoutWeightLarge },
|
||||
{ sats: config.payoutJackpotSats, weight: config.payoutWeightJackpot },
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Weighted random selection. Returns sats amount.
|
||||
*/
|
||||
export function selectWeightedPayout(): number {
|
||||
const buckets = getPayoutBuckets();
|
||||
const totalWeight = buckets.reduce((s, b) => s + b.weight, 0);
|
||||
let r = randomInt(0, totalWeight);
|
||||
for (const b of buckets) {
|
||||
if (r < b.weight) return b.sats;
|
||||
r -= b.weight;
|
||||
}
|
||||
return config.payoutSmallSats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute payout for this claim: weighted selection, capped by daily budget remaining.
|
||||
*/
|
||||
export function computePayoutForClaim(todayPaidSats: number): number {
|
||||
const remaining = Math.max(0, config.dailyBudgetSats - todayPaidSats);
|
||||
if (remaining < config.faucetMinSats) return 0;
|
||||
const selected = selectWeightedPayout();
|
||||
return Math.min(selected, remaining, config.faucetMaxSats);
|
||||
}
|
||||
|
||||
export interface CreateQuoteResult {
|
||||
quoteId: string;
|
||||
payoutSats: number;
|
||||
expiresAt: number;
|
||||
}
|
||||
|
||||
export async function createQuote(pubkey: string, lightningAddress: string): Promise<CreateQuoteResult | null> {
|
||||
const db = getDb();
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const dayStart = now - (now % 86400);
|
||||
const todayPaid = await db.getPaidSatsSince(dayStart);
|
||||
let payout = computePayoutForClaim(todayPaid);
|
||||
if (payout <= 0) return null;
|
||||
|
||||
const walletBalance = await getWalletBalanceSats();
|
||||
payout = Math.min(payout, Math.max(0, walletBalance));
|
||||
if (payout < config.faucetMinSats) return null;
|
||||
|
||||
const quoteId = uuidv4();
|
||||
const expiresAt = now + QUOTE_TTL_SECONDS;
|
||||
await db.createQuote(quoteId, pubkey, payout, lightningAddress, expiresAt);
|
||||
return { quoteId, payoutSats: payout, expiresAt };
|
||||
}
|
||||
Reference in New Issue
Block a user