// Synthetic local numbering: each height issues 5e9 consecutive sats. Fees retire // the unassigned input tail (not Bitcoin miner fee collection). Half-open ranges. export const LOCAL_REWARD = 5e9; export function sliceRanges(ranges, offset, length) { const result = []; for (const [start, end] of ranges) { const size = end - start; if (offset >= size) { offset -= size; continue; } const take = Math.min(length, size - offset); if (take) result.push([start + offset, start + offset + take]); length -= take; offset = 0; if (!length) break; } return result; } export function satAt(ranges, offset) { return sliceRanges(ranges, offset, 1)[0]?.[0]; } export function offsetOf(ranges, sat) { let offset = 0; for (const [start, end] of ranges) { if (sat >= start && sat < end) return offset + sat - start; offset += end - start; } return null; } export function outputRanges(tx, coins, height, coinbase) { const ranges = coinbase ? [[(height - 1) * LOCAL_REWARD, height * LOCAL_REWARD]] : coins.flatMap(c => c.ranges); let offset = 0; return tx.vout.map(out => { const result = sliceRanges(ranges, offset, Number(out.value)); offset += Number(out.value); return result; }); }