mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-06 04:42:57 +00:00
Merge pull request #316 from PR0M3TH3AN/codex/fix-bitcoin-zap-function-to-split-payments
Handle string zap platform fee inputs
This commit is contained in:
5 files changed
+153
-20
No files matched your search
@@ -0,0 +1,74 @@
|
||||
// js/payments/platformFee.js
|
||||
|
||||
import { PLATFORM_FEE_PERCENT } from "../config.js";
|
||||
|
||||
export function parsePercentValue(value) {
|
||||
if (typeof value === "number") {
|
||||
return Number.isFinite(value) ? value : NaN;
|
||||
}
|
||||
|
||||
if (typeof value === "bigint") {
|
||||
return Number(value);
|
||||
}
|
||||
|
||||
if (typeof value === "string") {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) {
|
||||
return NaN;
|
||||
}
|
||||
|
||||
if (trimmed.includes("/")) {
|
||||
const [firstRaw, secondRaw] = trimmed.split("/").map((part) => part.trim());
|
||||
if (firstRaw && secondRaw) {
|
||||
const first = Number(firstRaw);
|
||||
const second = Number(secondRaw);
|
||||
if (Number.isFinite(first) && Number.isFinite(second) && first >= 0 && second >= 0) {
|
||||
const total = first + second;
|
||||
if (total > 0) {
|
||||
return (second / total) * 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const percentStripped = trimmed.endsWith("%") ? trimmed.slice(0, -1).trim() : trimmed;
|
||||
const directNumeric = Number(percentStripped);
|
||||
if (Number.isFinite(directNumeric)) {
|
||||
return directNumeric;
|
||||
}
|
||||
|
||||
const match = percentStripped.match(/-?\d+(?:\.\d+)?/);
|
||||
if (match) {
|
||||
const fallback = Number(match[0]);
|
||||
return Number.isFinite(fallback) ? fallback : NaN;
|
||||
}
|
||||
|
||||
return NaN;
|
||||
}
|
||||
|
||||
return Number.isFinite(value) ? Number(value) : NaN;
|
||||
}
|
||||
|
||||
export function clampPercent(value) {
|
||||
const numeric = parsePercentValue(value);
|
||||
if (!Number.isFinite(numeric)) {
|
||||
return 0;
|
||||
}
|
||||
return Math.min(100, Math.max(0, numeric));
|
||||
}
|
||||
|
||||
export function getDefaultPlatformFeePercent() {
|
||||
const parsed = parsePercentValue(PLATFORM_FEE_PERCENT);
|
||||
if (!Number.isFinite(parsed)) {
|
||||
return 0;
|
||||
}
|
||||
return clampPercent(parsed);
|
||||
}
|
||||
|
||||
export function resolvePlatformFeePercent(overrideValue) {
|
||||
const parsedOverride = parsePercentValue(overrideValue);
|
||||
if (Number.isFinite(parsedOverride)) {
|
||||
return clampPercent(parsedOverride);
|
||||
}
|
||||
return getDefaultPlatformFeePercent();
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PLATFORM_FEE_PERCENT } from "../config.js";
|
||||
import { resolvePlatformFeePercent } from "./platformFee.js";
|
||||
import {
|
||||
resolveLightningAddress as defaultResolveLightningAddress,
|
||||
fetchPayServiceData as defaultFetchPayServiceData,
|
||||
@@ -112,11 +112,9 @@ export async function fetchLightningMetadata(
|
||||
|
||||
export function calculateZapShares(amount, overrideFee = null) {
|
||||
const numericAmount = Math.max(0, Math.round(Number(amount) || 0));
|
||||
const percentSource =
|
||||
typeof overrideFee === "number" && Number.isFinite(overrideFee)
|
||||
? overrideFee
|
||||
: PLATFORM_FEE_PERCENT;
|
||||
const feePercent = Math.min(100, Math.max(0, Math.round(percentSource)));
|
||||
const feePercent = resolvePlatformFeePercent(
|
||||
typeof overrideFee === "undefined" ? null : overrideFee
|
||||
);
|
||||
const platformShare = Math.floor((numericAmount * feePercent) / 100);
|
||||
const creatorShare = numericAmount - platformShare;
|
||||
return {
|
||||
|
||||
+9
-14
@@ -1,6 +1,6 @@
|
||||
// js/payments/zapSplit.js
|
||||
|
||||
import { PLATFORM_FEE_PERCENT, ADMIN_SUPER_NPUB } from "../config.js";
|
||||
import { ADMIN_SUPER_NPUB } from "../config.js";
|
||||
import {
|
||||
resolveLightningAddress,
|
||||
fetchPayServiceData,
|
||||
@@ -9,6 +9,11 @@ import {
|
||||
} from "./lnurl.js";
|
||||
import { ensureWallet, sendPayment } from "./nwcClient.js";
|
||||
import { getPlatformLightningAddress } from "./platformAddress.js";
|
||||
import {
|
||||
clampPercent,
|
||||
parsePercentValue,
|
||||
resolvePlatformFeePercent,
|
||||
} from "./platformFee.js";
|
||||
|
||||
const HEX64_REGEX = /^[0-9a-f]{64}$/i;
|
||||
const ZAP_KIND = 9734;
|
||||
@@ -24,19 +29,7 @@ const DEFAULT_DEPS = Object.freeze({
|
||||
});
|
||||
|
||||
function getOverridePlatformFee() {
|
||||
const override = globalThis?.__BITVID_PLATFORM_FEE_OVERRIDE__;
|
||||
if (Number.isFinite(override)) {
|
||||
return override;
|
||||
}
|
||||
return PLATFORM_FEE_PERCENT;
|
||||
}
|
||||
|
||||
function clampPercent(value) {
|
||||
const numeric = Number(value);
|
||||
if (!Number.isFinite(numeric)) {
|
||||
return 0;
|
||||
}
|
||||
return Math.min(100, Math.max(0, numeric));
|
||||
return resolvePlatformFeePercent(globalThis?.__BITVID_PLATFORM_FEE_OVERRIDE__);
|
||||
}
|
||||
|
||||
function sanitizeAmount(amount) {
|
||||
@@ -341,4 +334,6 @@ export const __TESTING__ = Object.freeze({
|
||||
determineRecipientPubkey,
|
||||
derivePointerTag,
|
||||
mergeDependencies,
|
||||
parsePercentValue,
|
||||
clampPercent,
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const sharedStateModule = await import("../js/payments/zapSharedState.js");
|
||||
const feeModule = await import("../js/payments/platformFee.js");
|
||||
|
||||
const { calculateZapShares } = sharedStateModule;
|
||||
const { resolvePlatformFeePercent } = feeModule;
|
||||
|
||||
(function testCalculateZapSharesSupportsRatioOverride() {
|
||||
const result = calculateZapShares(35, "70/30");
|
||||
assert.equal(result.total, 35);
|
||||
assert.equal(result.platformShare, 10);
|
||||
assert.equal(result.creatorShare, 25);
|
||||
assert.equal(result.feePercent, 30);
|
||||
})();
|
||||
|
||||
(function testCalculateZapSharesHandlesPercentStrings() {
|
||||
const result = calculateZapShares(200, "30% ");
|
||||
assert.equal(result.platformShare, 60);
|
||||
assert.equal(result.creatorShare, 140);
|
||||
assert.equal(result.feePercent, 30);
|
||||
})();
|
||||
|
||||
(function testResolvePlatformFeePercentFallsBackToDefault() {
|
||||
const percent = resolvePlatformFeePercent("not-a-number");
|
||||
assert.equal(percent, 0);
|
||||
})();
|
||||
|
||||
console.log("zap-shared-state tests passed");
|
||||
@@ -180,6 +180,42 @@ async function testSplitMath() {
|
||||
delete globalThis.__BITVID_PLATFORM_FEE_OVERRIDE__;
|
||||
}
|
||||
|
||||
async function testStringFeeOverride() {
|
||||
const videoEvent = {
|
||||
id: "event-id",
|
||||
pubkey: "c".repeat(64),
|
||||
lightningAddress: "[email protected]",
|
||||
tags: [["d", "pointer"]],
|
||||
kind: 30078,
|
||||
};
|
||||
|
||||
globalThis.__BITVID_PLATFORM_FEE_OVERRIDE__ = "70/30";
|
||||
const seventyThirty = createDeps();
|
||||
const ratioResult = await splitAndZap(
|
||||
{ videoEvent, amountSats: 1000, comment: "Great video!" },
|
||||
seventyThirty.deps
|
||||
);
|
||||
|
||||
assert.equal(ratioResult.creatorShare, 700);
|
||||
assert.equal(ratioResult.platformShare, 300);
|
||||
const ratioSendCalls = seventyThirty.getSendCalls();
|
||||
assert.equal(ratioSendCalls.length, 2, "should send two payments for ratio");
|
||||
assert.equal(ratioSendCalls[0].amountSats, 700);
|
||||
assert.equal(ratioSendCalls[1].amountSats, 300);
|
||||
|
||||
globalThis.__BITVID_PLATFORM_FEE_OVERRIDE__ = "30%";
|
||||
const percentOverride = createDeps();
|
||||
const percentResult = await splitAndZap(
|
||||
{ videoEvent, amountSats: 200, comment: "Another zap" },
|
||||
percentOverride.deps
|
||||
);
|
||||
|
||||
assert.equal(percentResult.creatorShare, 140);
|
||||
assert.equal(percentResult.platformShare, 60);
|
||||
|
||||
delete globalThis.__BITVID_PLATFORM_FEE_OVERRIDE__;
|
||||
}
|
||||
|
||||
async function testLnurlBounds() {
|
||||
globalThis.__BITVID_PLATFORM_FEE_OVERRIDE__ = 0;
|
||||
const depsWrapper = createDeps({ minSendable: 5_000 });
|
||||
@@ -265,5 +301,6 @@ await testSplitMath();
|
||||
await testLnurlBounds();
|
||||
await testMissingAddress();
|
||||
await testWalletFailure();
|
||||
await testStringFeeOverride();
|
||||
|
||||
console.log("zap-split tests passed");
|
||||
Reference in new issue
Block a user