Repository navigation
test(sdk): pin toBaseUnits behaviour at the i128 and NUMERIC(39,0) boundaries - #40
Open
jdjioe5-cpu wants to merge 1 commit into
Open
jdjioe5-cpu wants to merge 1 commit into
jdjioe5-cpu wants to merge 1 commit into
Conversation
jdjioe5-cpu
force-pushed
the
test/amount-precision-boundary
branch
from
October 17, 2026 14:07
09f4c52 to
3ee9ace
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
Closes #36.
Pins the amount precision and boundary behaviour across the SDK and database write paths at the
i128andNUMERIC(39,0)limits per roadmap rowM2-21. Characterises as-is behaviour oftoBaseUnitsandfromBaseUnitsat2^127 - 1,2^127, 39 digits, 40 digits, andi128::MAX / 10^7human amounts without rounding, and verifies Postgres numeric overflow behaviour on the write path.Hard-rule and doc review:
docs/ARCHITECTURE_ESSENTIALS.md§4 (USDC via SAC, 7 decimals,i128) and §7 (Amounts areNUMERIC(39,0)andbigintin code)AGENTS.md§3 (Hard rules 10: Minimum viable; 11: Country-agnostic core)AGENTS.md§8.2 (kinlock-sdk: amounts arebigintin code, decimal strings in JSON,NUMERIC(39,0)in Postgres; never usenumberfor money; no public API additions without approval)Changes
packages/sdk/src/__tests__/format.test.ts: added boundary characterisation suite covering2^127 - 1,2^127, 39-digitNUMERIC(39,0)boundary, 40-digit overflow,i128::MAX / 10^7plain integer amounts, negative inputs, and sub-unit fraction rejection.services/indexer/test/ingest.test.ts: added write-path test proving a 39-digit amount (10^39 - 1) succeeds withinNUMERIC(39,0), while a 40-digit amount (10^39) is rejected on the write path with Postgres numeric field overflow (less than 10^39, SQLSTATE 22003).ROADMAP.md: markedM2-21asDONE, added Changelog entry, and updatedLast updated.As-Is Boundary Characterisation Table (from actual test run)
bigint)fromBaseUnits2^127 - 1(i128::MAX)"17014118346046923173168730371588.4105727"170141183460469231731687303715884105727n"17014118346046923173168730371588.4105727"i128, and PostgresNUMERIC(39,0). Exact round-trip.2^127(one abovei128::MAX)"17014118346046923173168730371588.4105728"170141183460469231731687303715884105728n"17014118346046923173168730371588.4105728"10^39 - 1(39 digits)"99999999999999999999999999999999.9999999"999999999999999999999999999999999999999n"99999999999999999999999999999999.9999999"NUMERIC(39,0)column on write path.10^39(40 digits)"100000000000000000000000000000000"1000000000000000000000000000000000000000n"100000000000000000000000000000000"less than 10^39, error code 22003).i128::MAX / 10^7(plain integer human amount)"17014118346046923173168730371588"170141183460469231731687303715880000000n"17014118346046923173168730371588"<= i128::MAX).i128::MAX / 10^7"17014118346046923173168730371589"170141183460469231731687303715890000000n"17014118346046923173168730371589"> i128::MAX); rejected by contract i128 overflow."-1","-0.1"toBaseUnitswithINVALID_AMOUNT.fromBaseUnitsformats negative delta ("-0.00001")."0.00000001"(8 decimals)INVALID_AMOUNT("amounts are never rounded").Hard rules / invariants touched
numbertype on any money path.Tests
Commands executed locally and results:
Docs / ADR
None needed (no public API changes).
Roadmap
M2-21:TODO->DONEAssumptions and open questions
toBaseUnitsrelies on pure BigInt parsing without artificially capping ati128::MAX; amounts exceedingi128::MAXfail at the Soroban transaction / contract layer, and amounts exceeding 39 digits fail at the Postgres column layer. As noted in issue Pin the amount precision boundary: what toBaseUnits refuses at the i128 and NUMERIC(39,0) limits #36, both outcomes are acceptable and prevent silent data corruption.