Skip to content

fix: use exact decimal math and fix the displayed swap rate (Closes #159) - #164

Open
waterWang wants to merge 1 commit into
Nodus-protocol:mainfrom
waterWang:fix/swap-rate-exact-decimals
Open

fix: use exact decimal math and fix the displayed swap rate (Closes #159)#164
waterWang wants to merge 1 commit into
Nodus-protocol:mainfrom
waterWang:fix/swap-rate-exact-decimals

Conversation

@waterWang

Copy link
Copy Markdown

Summary

Fixes the swap preview rate bug: it previously rendered the entire quoted output amount as 1 tokenIn = ..., instead of computing output ÷ input. Also replaces all floating-point Number arithmetic in the financial path with exact BigInt decimal math.

Closes #159

Type of Change

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • Tests
  • Chore / tooling

What Changed

New: lib/math.ts

  • toBaseUnits(display, decimals) — parse human input into integer base units with BigInt only: no Number coercion, no scientific-notation / truncation surprises, excess decimals truncated safely.
  • fromBaseUnits(base, decimals, maxDecimals) — exact base-unit → display formatting.
  • computeRate(amountOut, amountIn, outDecimals, inDecimals) — rate = output ÷ input with the correct decimal scale in both directions (e.g. 10 USDC → 1 XLM = 10 USDC/XLM and reversed). Uses integer division with a scaled dividend, never floating point.
  • applySlippage(amountOut, slippageBps) — integer basis-point minimum-output math, explicitly rounding down (favorable to safety).
  • MathError — structured error with a machine-readable tag (invalid / overflow / division_by_zero).
  • roundTripDisplay — property-test helper verifying display → base → display round trips.

lib/format.ts

  • stroopsToXlm / rawToUsdc now use BigInt division through formatDecimals instead of Number / — exact for every input, including values beyond Number.MAX_SAFE_INTEGER.
  • xlmToStroops / usdcToRaw delegate to toBaseUnits.

components/SwapPreview.tsx

  • Rate row now shows computeRate(amountOut, amountIn, ...) instead of the raw output amount — the actual per-1-unit exchange rate.

app/swap/page.tsx

  • minAmountOut now uses applySlippage() (documented rounding direction) instead of inline BigInt.

jest.setup.ts + jest.config.js

  • Sets IS_REACT_ACT_ENVIRONMENT and NODE_ENV=test so React 19 + jsdom component tests stop crashing with React.act is not a function.

How Was It Tested?

  • New __tests__/lib/math.test.ts: 22 cases covering base-unit conversion, exact large/small amounts, rate both directions, boundary ratios, slippage rounding direction, invalid input, and round-trip property checks.
  • __tests__/lib/format.test.ts: unchanged expectations all pass (BigInt refactor is behavior-preserving).
  • Full suite: 55 tests, 6 suites, all passing (NODE_ENV=development npx jest).
  • npx tsc --noEmit — clean.
  • npm run lint (eslint) — no errors.

Checklist

  • My code follows the style guidelines in CONTRIBUTING.md
  • I have rebased onto the latest main (no merge commits)
  • I have added tests for my changes
  • All existing tests pass locally
  • I have linked the issue above (Closes #159)
  • I have updated documentation if needed

- Add lib/math.ts: toBaseUnits, fromBaseUnits, computeRate, applySlippage
- Fix lib/format.ts: use BigInt instead of Number arithmetic
- Fix SwapPreview.tsx: rate = output / input (not raw output amount)
- Fix swap/page.tsx: use applySlippage for minAmountOut
- Add jest.setup.ts for React 19 act-compat
- Add __tests__/lib/math.test.ts: 22 cases + round-trip property tests

Closes Nodus-protocol#159
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.

correctness: use exact decimal math and fix the displayed swap rate

1 participant