From 0bfe75e4fc84a22f57c0208c297a0d480b122f96 Mon Sep 17 00:00:00 2001 From: Sertug17 <104278804+Sertug17@users.noreply.github.com> Date: Sun, 16 Aug 2026 14:33:37 +0300 Subject: [PATCH] fix: cache nowSeconds() and handle negative BigInt in format functions - Cache nowSeconds() in sessionItem to prevent TOCTOU race producing negative remaining_secs - Handle negative BigInt values in formatTokenUnits and formatCreditBalance Fixes tempoxyz#87 Fixes tempoxyz#88 --- src/commands/sessions.ts | 3 ++- src/shared/utils.ts | 27 ++++++++++++++++++--------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/commands/sessions.ts b/src/commands/sessions.ts index b2fbc10..c0383bd 100644 --- a/src/commands/sessions.ts +++ b/src/commands/sessions.ts @@ -658,6 +658,7 @@ function sessionItem(record: ChannelRecord) { record.accepted_cumulative > 0n ? record.accepted_cumulative : record.cumulative_amount; const remaining = record.deposit > spent ? record.deposit - spent : 0n; const status = sessionStatus(record); + const now = nowSeconds(); return { channel_id: record.channel_id, network: record.network, @@ -670,7 +671,7 @@ function sessionItem(record: ChannelRecord) { ...(status === "closing" || status === "finalizable" ? { remaining_secs: - record.grace_ready_at > nowSeconds() ? record.grace_ready_at - nowSeconds() : 0, + record.grace_ready_at > now ? record.grace_ready_at - now : 0, } : {}), created_at: formatUnixTimestamp(record.created_at), diff --git a/src/shared/utils.ts b/src/shared/utils.ts index 48c0eb2..92b4281 100644 --- a/src/shared/utils.ts +++ b/src/shared/utils.ts @@ -38,20 +38,29 @@ export function formatMicroUnits(value: string) { } export function formatTokenUnits(value: bigint, decimals: number) { + const negative = value < 0n; + const abs = negative ? -value : value; const divisor = 10n ** BigInt(decimals); - const whole = value / divisor; - const fractional = value % divisor; - if (decimals === 0) return whole.toString(); - if (fractional === 0n) return `${whole}.${"0".repeat(decimals)}`; - return `${whole}.${fractional.toString().padStart(decimals, "0")}`; + const whole = abs / divisor; + const fractional = abs % divisor; + const formatted = + decimals === 0 + ? whole.toString() + : fractional === 0n + ? `${whole}.${"0".repeat(decimals)}` + : `${whole}.${fractional.toString().padStart(decimals, "0")}`; + return negative ? `-${formatted}` : formatted; } export function formatCreditBalance(rawBalance: bigint) { + const negative = rawBalance < 0n; + const abs = negative ? -rawBalance : rawBalance; const divisor = 10_000n; - const whole = rawBalance / divisor; - const fractional = rawBalance % divisor; - if (fractional === 0n) return whole.toString(); - return `${whole}.${fractional.toString().padStart(4, "0")}`; + const whole = abs / divisor; + const fractional = abs % divisor; + const formatted = + fractional === 0n ? whole.toString() : `${whole}.${fractional.toString().padStart(4, "0")}`; + return negative ? `-${formatted}` : formatted; } export function isChannelId(value: string) {