Skip to content

fix: improve float handling in calldata encoder#199

Open
ygd58 wants to merge 1 commit into
genlayerlabs:v2-devfrom
ygd58:fix/float-calldata-encoding
Open

fix: improve float handling in calldata encoder#199
ygd58 wants to merge 1 commit into
genlayerlabs:v2-devfrom
ygd58:fix/float-calldata-encoding

Conversation

@ygd58

@ygd58 ygd58 commented Jul 9, 2026

Copy link
Copy Markdown

Summary

Improves float value handling in the calldata encoder.

Problem

Currently, passing any number with a fractional part throws a generic error: invalid calldata input. This is unhelpful and also incorrectly rejects floats like 1.0 that are safely representable as integers.

Fixes #27

Changes

  • Float values with no fractional part (e.g. 1.0, 2.0) are now silently coerced to their integer equivalent and encoded correctly
  • True floats (e.g. 1.5) now throw a descriptive error message telling the user to convert to integer or string
  • Added unit tests covering all cases

Tests

✓ calldata encoder - float handling (4 tests)
  ✓ should encode integer numbers correctly
  ✓ should encode float with no fractional part as integer (e.g. 1.0)
  ✓ should throw descriptive error for true float values
  ✓ should throw descriptive error for NaN

Summary by CodeRabbit

  • Bug Fixes
    • Improved number handling during calldata encoding so whole-number values like 1.0 are accepted and encoded correctly.
    • Fractional values, NaN, and infinities now fail with a clearer error message that explains how to proceed.
    • Added test coverage for integer-like floats, true decimals, and invalid numeric values.

- Encode floats with no fractional part (e.g. 1.0) as integers
- Replace generic error with descriptive message for true floats
- Add unit tests for float encoding edge cases

Fixes genlayerlabs#27
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The calldata encoder's number-handling logic was modified to accept finite numbers with no fractional component by truncating them, while throwing a descriptive error for true fractional floats, NaN, or infinite values. A new test suite validates integer encoding, whole-number float coercion, and error cases.

Changes

Float number encoding fix

Layer / File(s) Summary
Number validation and truncation
src/abi/calldata/encoder.ts
Non-integer number handling now accepts finite whole-number floats by truncating via Math.trunc, and throws a new descriptive error for fractional floats, NaN, or infinities instead of always rejecting non-integers.
Float handling tests
tests/calldata-encoder.test.ts
New Vitest suite verifies integer round-tripping, 1.0-style float coercion to integer, error throwing for fractional floats with the expected message, and throwing for NaN.

Estimated code review effort: 1 (Trivial) | ~5 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning Issue #27 expected float args like 0.5 and 3.22 to serialize successfully, but this PR still rejects true fractional floats. Update calldata encoding to serialize fractional float arguments for contract calls instead of throwing, and add tests for those cases.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change to calldata float handling and matches the modified files.
Description check ✅ Passed The description covers the problem, change summary, linked issue, and testing, which is mostly complete for the template.
Out of Scope Changes check ✅ Passed The code changes and tests stay focused on calldata float handling and do not introduce unrelated scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions
github-actions Bot changed the base branch from main to v2-dev July 9, 2026 13:22
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

This PR targeted main, which is only the default/static branch.

I retargeted it to v2-dev, the active development branch. Pushes to v2-dev automatically fast-forward main.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
tests/calldata-encoder.test.ts (1)

18-26: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add edge case tests for Infinity, -Infinity, and negative fractional floats.

Infinity and -Infinity hit the same !Number.isInteger!Number.isFinite → throw path as NaN, but aren't tested. A negative fractional float like -1.5 would verify the error message includes the correct value.

✨ Suggested additional tests
   it("should throw descriptive error for NaN", () => {
     expect(() => encode(NaN)).toThrow();
   });
+
+  it("should throw descriptive error for Infinity", () => {
+    expect(() => encode(Infinity)).toThrow();
+  });
+
+  it("should throw descriptive error for negative fractional floats", () => {
+    expect(() => encode(-1.5)).toThrow(
+      "calldata encoding error: float value '-1.5' is not supported"
+    );
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/calldata-encoder.test.ts` around lines 18 - 26, Add edge case coverage
in the calldata encoder tests by extending the existing `encode` expectations
alongside the `NaN` case. Add assertions for `Infinity` and `-Infinity` to
verify they hit the same non-finite error path, and add a negative fractional
float such as `-1.5` to confirm the thrown message includes the exact value. Use
the current `calldata-encoder.test.ts` `encode(...)` test block as the place to
locate and expand these cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/abi/calldata/encoder.ts`:
- Around line 89-98: The dead whole-number float coercion branch in `encodeNum`
within `calldata` encoding is unreachable because `Number.isInteger` already
accepts values like `1.0`; remove the `Number.isFinite`/`Math.floor` path and
keep the existing integer handling, while preserving the improved error message
in the non-integer `throw` branch for `src/abi/calldata/encoder.ts`.

In `@tests/calldata-encoder.test.ts`:
- Around line 2-3: Update the imports in the test file to use the project path
alias for anything coming from src instead of relative paths. Specifically,
adjust the encode and decode imports in the calldata encoder test to reference
the aliased src location, following the same `@/`* convention used elsewhere in
the codebase.

---

Nitpick comments:
In `@tests/calldata-encoder.test.ts`:
- Around line 18-26: Add edge case coverage in the calldata encoder tests by
extending the existing `encode` expectations alongside the `NaN` case. Add
assertions for `Infinity` and `-Infinity` to verify they hit the same non-finite
error path, and add a negative fractional float such as `-1.5` to confirm the
thrown message includes the exact value. Use the current
`calldata-encoder.test.ts` `encode(...)` test block as the place to locate and
expand these cases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 77e4a583-042f-42db-90d6-b94f562ed94b

📥 Commits

Reviewing files that changed from the base of the PR and between 1b7f50a and 5081eac.

📒 Files selected for processing (2)
  • src/abi/calldata/encoder.ts
  • tests/calldata-encoder.test.ts

Comment on lines 89 to +98
if (!Number.isInteger(data)) {
reportError("floats are not supported", data);
if (Number.isFinite(data) && Math.floor(data) === data) {
// Safe: float with no fractional part (e.g. 1.0 → 1)
encodeNum(to, BigInt(Math.trunc(data)));
return;
}
throw new Error(
`calldata encoding error: float value '${data}' is not supported. ` +
`Convert to an integer or pass as a string instead.`
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Dead code: the whole-number float coercion branch is unreachable.

Number.isInteger(1.0) returns true in JavaScript because 1.0 === 1. The Number.isInteger specification checks Math.floor(x) === x for finite numbers — the same condition at line 90. Therefore, when !Number.isInteger(data) is true, Math.floor(data) === data is guaranteed false for all finite numbers, making lines 90–94 unreachable.

The PR's stated goal of handling 1.0 is already achieved by the existing Number.isInteger check at line 89 routing to line 100. The new code never executes.

🔧 Proposed fix: remove dead branch, keep improved error message
     case "number": {
       if (!Number.isInteger(data)) {
-        if (Number.isFinite(data) && Math.floor(data) === data) {
-          // Safe: float with no fractional part (e.g. 1.0 → 1)
-          encodeNum(to, BigInt(Math.trunc(data)));
-          return;
-        }
         throw new Error(
           `calldata encoding error: float value '${data}' is not supported. ` +
           `Convert to an integer or pass as a string instead.`
         );
       }
       encodeNum(to, BigInt(data));
       return;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!Number.isInteger(data)) {
reportError("floats are not supported", data);
if (Number.isFinite(data) && Math.floor(data) === data) {
// Safe: float with no fractional part (e.g. 1.0 → 1)
encodeNum(to, BigInt(Math.trunc(data)));
return;
}
throw new Error(
`calldata encoding error: float value '${data}' is not supported. ` +
`Convert to an integer or pass as a string instead.`
);
if (!Number.isInteger(data)) {
throw new Error(
`calldata encoding error: float value '${data}' is not supported. ` +
`Convert to an integer or pass as a string instead.`
);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/abi/calldata/encoder.ts` around lines 89 - 98, The dead whole-number
float coercion branch in `encodeNum` within `calldata` encoding is unreachable
because `Number.isInteger` already accepts values like `1.0`; remove the
`Number.isFinite`/`Math.floor` path and keep the existing integer handling,
while preserving the improved error message in the non-integer `throw` branch
for `src/abi/calldata/encoder.ts`.

Comment on lines +2 to +3
import { encode } from "../src/abi/calldata/encoder";
import { decode } from "../src/abi/calldata/decoder";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use path aliases @/* for src/ imports per coding guidelines.

As per coding guidelines: **/*.{ts,tsx}: Use path alias @/* for imports from src/ directory.

♻️ Proposed fix
-import { encode } from "../src/abi/calldata/encoder";
-import { decode } from "../src/abi/calldata/decoder";
+import { encode } from "`@/abi/calldata/encoder`";
+import { decode } from "`@/abi/calldata/decoder`";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { encode } from "../src/abi/calldata/encoder";
import { decode } from "../src/abi/calldata/decoder";
import { encode } from "`@/abi/calldata/encoder`";
import { decode } from "`@/abi/calldata/decoder`";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/calldata-encoder.test.ts` around lines 2 - 3, Update the imports in the
test file to use the project path alias for anything coming from src instead of
relative paths. Specifically, adjust the encode and decode imports in the
calldata encoder test to reference the aliased src location, following the same
`@/`* convention used elsewhere in the codebase.

Source: Coding guidelines

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

This PR targeted main, which is only the default/static branch.

I retargeted it to v2-dev, the active development branch. Pushes to v2-dev automatically fast-forward main.

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.

fix(genlayer-js): TypeError when passing float values in writeContract args

1 participant