From 8fbac3271781b18d646ffadf1f1b33bb5c7c62aa Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 18 Mar 2026 20:48:25 +0000 Subject: [PATCH] fix: No upper-bound validation for ci/anchorShare/discoveryDepth outputs (#960) Add assertUint256Max1e18 validator in index.ts and apply it to the ci, anchorShare, and discoveryDepth output literals. Programs emitting values > 1e18 for these fields now fail with a clear transpiler-level error instead of silently violating LiquidityManager invariants at runtime. Add tests 12-14 in test_transpiler_clamping.sh covering the over-range rejection for each of the three fields. Co-Authored-By: Claude Sonnet 4.6 --- tools/push3-transpiler/src/index.ts | 20 +++++++++ .../test_transpiler_clamping.sh | 45 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/tools/push3-transpiler/src/index.ts b/tools/push3-transpiler/src/index.ts index 2a0bc01..5f0a1ac 100644 --- a/tools/push3-transpiler/src/index.ts +++ b/tools/push3-transpiler/src/index.ts @@ -51,6 +51,23 @@ function assertUint24Range(value: string, name: string): void { } } +/** + * Validates that a transpiler output variable for a uint256 field scaled to 1e18 + * does not exceed 1e18 when it is a literal constant. Values > 1e18 violate + * LiquidityManager invariants (these fields represent fractions in [0, 1]). + */ +function assertUint256Max1e18(value: string, name: string): void { + if (INT_LITERAL_RE.test(value)) { + const n = BigInt(value); + if (n > 1000000000000000000n) { + throw new Error( + `Transpiler error: ${name} literal (${value}) exceeds 1e18 (max allowed). ` + + `Fix the Push3 program to produce a value in [0, 1e18] for ${name}.`, + ); + } + } +} + function main(): void { const args = process.argv.slice(2); if (args.length < 2) { @@ -70,9 +87,12 @@ function main(): void { const { functionBody, ciVar, anchorShareVar, anchorWidthVar, discoveryDepthVar } = transpile(ast); assertNonNegativeLiteral(ciVar, 'ci'); + assertUint256Max1e18(ciVar, 'ci'); assertNonNegativeLiteral(anchorShareVar, 'anchorShare'); + assertUint256Max1e18(anchorShareVar, 'anchorShare'); assertUint24Range(anchorWidthVar, 'anchorWidth'); assertNonNegativeLiteral(discoveryDepthVar, 'discoveryDepth'); + assertUint256Max1e18(discoveryDepthVar, 'discoveryDepth'); const solidityLines = [ '// SPDX-License-Identifier: GPL-3.0-or-later', diff --git a/tools/push3-transpiler/test_transpiler_clamping.sh b/tools/push3-transpiler/test_transpiler_clamping.sh index 48155c2..5e9d229 100755 --- a/tools/push3-transpiler/test_transpiler_clamping.sh +++ b/tools/push3-transpiler/test_transpiler_clamping.sh @@ -217,6 +217,51 @@ else fi fi +# ── Test 12: ci literal > 1e18 → transpiler error ──────────────────────────── +echo "Test 12: ci = 2e18 (literal) → transpiler error" + +STDERR=$(run_transpiler "( + 300000000000000000 + 100 + 300000000000000000 + 2000000000000000000 +)") + +assert_contains \ + "ci 2e18: transpiler emits max1e18 error message" \ + "$STDERR" \ + "ci literal (2000000000000000000) exceeds 1e18 (max allowed)" + +# ── Test 13: anchorShare literal > 1e18 → transpiler error ─────────────────── +echo "Test 13: anchorShare = 2e18 (literal) → transpiler error" + +STDERR=$(run_transpiler "( + 300000000000000000 + 100 + 2000000000000000000 + 0 +)") + +assert_contains \ + "anchorShare 2e18: transpiler emits max1e18 error message" \ + "$STDERR" \ + "anchorShare literal (2000000000000000000) exceeds 1e18 (max allowed)" + +# ── Test 14: discoveryDepth literal > 1e18 → transpiler error ──────────────── +echo "Test 14: discoveryDepth = 2e18 (literal) → transpiler error" + +STDERR=$(run_transpiler "( + 2000000000000000000 + 100 + 300000000000000000 + 0 +)") + +assert_contains \ + "discoveryDepth 2e18: transpiler emits max1e18 error message" \ + "$STDERR" \ + "discoveryDepth literal (2000000000000000000) exceeds 1e18 (max allowed)" + # ── Summary ────────────────────────────────────────────────────────────────── echo "" echo "Results: $PASS passed, $FAIL failed"