Skip to content

Alfie gf - #461

Merged
Wilfred007 merged 6 commits into
Protocol-Guild:mainfrom
alfeedrips:alfie-gf
Aug 16, 2026
Merged

Alfie gf#461
Wilfred007 merged 6 commits into
Protocol-Guild:mainfrom
alfeedrips:alfie-gf

Conversation

@alfeedrips

Copy link
Copy Markdown
Contributor

Summary
Introduces a shared Money fixed-point type for payroll financial math, eliminating JS floating-point drift across tax, benefits, and bonus calculations. Replaces the repeated parseFloat((...).toFixed(7)) + float += anti-pattern with BigInt-based arithmetic that guarantees exact results.
Changes

  • backend/src/utils/money.ts (new) — BigInt-based Money class storing values scaled to 7 decimal places (Stellar stroop precision). Supports add, sub, mul, div, percentage, max, isNegative, isZero, toNumber, toFixed.
  • backend/src/utils/tests/money.test.ts (new) — 21 tests covering construction, arithmetic, percentage, comparison, and drift regression tests proving gross === totalTax + net invariant preservation.
  • backend/src/services/taxService.ts — calculateDeductionsRuleBased and generateReport migrated to Money.
  • backend/src/services/benefitsService.ts — generateDraftPayslip migrated to Money for deduction/tax calculations and total accumulation.
  • backend/src/services/payrollBonusService.ts — getPayrollRunSummary migrated to Money for amount summation.
    Testing
    npx jest src/services/tests/taxService.test.ts src/utils/tests/money.test.ts --no-coverage
  • 39 tests passing (18 taxService + 21 money)
  • Drift regression tests verify gross === totalTax + net exactly across 10 and 20 stacked percentage deductions with fractional rates
  • All existing taxService tests pass with correct (not just similar) values
    Tradeoffs
  • No external dependency — rolled a minimal Money class instead of adding decimal.js or big.js. The class is 76 lines, covers exactly the operations used in the three services, and avoids bundle bloat. If more complex decimal math is needed later, swapping to a library is straightforward since the API surface is similar.
  • 7-decimal precision hardcoded — matches Stellar stroop precision. Could be made configurable if other precision requirements emerge.
  • toNumber() at boundaries — the services still return number at their public API boundaries (to callers, DB, etc.). The Money class eliminates drift within the calculation; the final toNumber() is a single rounding step, not N accumulated ones.
    Architecture
    backend/src/utils/money.ts ← shared Money type (BigInt, 7 decimals)
    backend/src/services/taxService.ts ← uses Money for deduction rules
    backend/src/services/benefitsService.ts ← uses Money for payslip generation
    backend/src/services/payrollBonusService.ts ← uses Money for summary totals
    Out of scope
  • Contract-side (Rust) math — tracked separately (Add property tests for revenue_split dust handling and basis-point rounding #398)
  • Non-financial numeric calculations elsewhere in the backend
  • Other services not mentioned in the issue
    Closes Financial calculations use floating-point math instead of fixed-point/decimal — cumulative rounding drift #451

“alfeedrips” added 6 commits August 16, 2026 03:12
Introduces a BigInt-based Money class that stores values scaled to 7
decimal places, eliminating JS floating-point drift in payroll
calculations. Supports arithmetic, percentage, and comparison
operations without intermediate rounding loss.
18 tests covering construction, arithmetic, percentage, comparison,
and a regression test that verifies 10 stacked percentage deductions
sum exactly to the expected total with no floating-point drift.
Replaces parseFloat/toFixed(7) arithmetic in calculateDeductionsRuleBased
and generateReport with Money operations. Eliminates floating-point drift
in stacked tax rule sums. All 18 existing taxService tests pass.
Replaces round7/parseFloat arithmetic in generateDraftPayslip with
Money.percentage and Money.from for deduction rule and tax rule
calculations, and uses Money for accumulating total deductions.
Replaces .reduce with parseFloat/.toFixed(7) accumulation in
getPayrollRunSummary with Money-based summing for base, bonus,
and total amounts.
Tests verify that Money preserves gross === total + net exactly across
10 and 20 stacked percentage deductions with fractional rates, and that
the old parseFloat/toFixed pattern produces the correct result for the
specific test case while Money guarantees it for all inputs.
@Wilfred007
Wilfred007 merged commit d035223 into Protocol-Guild:main Aug 16, 2026
1 check passed
@Wilfred007

Copy link
Copy Markdown
Contributor

Thank you @alfeedrips for your contribution

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Financial calculations use floating-point math instead of fixed-point/decimal — cumulative rounding drift

2 participants