Skip to content

fix: decode sign-extended narrow signed integers - #68

Merged
manuelwedler merged 4 commits into
sourcifyeth:mainfrom
emdin:fix/sign-extended-int-decoding
Sep 10, 2026
Merged

manuelwedler merged 4 commits into
sourcifyeth:mainfrom
emdin:fix/sign-extended-int-decoding

Conversation

@emdin

@emdin emdin commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

ABI calldata containing a negative narrow signed integer currently renders an incorrect positive value. For example, an int24 Uniswap tick of -198310, sign-extended to a 32-byte ABI word, is decoded by subtracting 2^24 from the entire 256-bit unsigned word.

Use BigInt.asIntN to normalize the ABI word to the declared bit width and interpret its sign. Positive integers, 256-bit integers and natural-width byte inputs retain their existing meaning.

Validation: 327 upstream tests pass, including 27 new regression cases spanning int8/int24/int64/int128/int256 boundaries, realistic negative Uniswap ticks and natural-width/empty inputs. An independent registry test runner with a real account descriptor improves from 18/21 cases to 21/21 with this patch; only the three signed-tick cases change.

Build, TypeScript checks, formatting and ESLint checks pass.

Comment thread test/signed-integers.spec.ts Outdated
@@ -0,0 +1,28 @@
import { describe, expect, it } from "vitest";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Test files in this repo are named after the module they test (formatters.spec.ts, fields.spec.ts). bytesToSignedBigInt lives in src/utils.ts, so please move these tests to a new test/utils.spec.ts. Please also add that file to the test file list in AGENTS.md.

Comment thread test/signed-integers.spec.ts Outdated
Comment on lines +2 to +7
import { bytesToSignedBigInt } from "../src/utils.js";

function abiWord(value: bigint): Uint8Array {
const hex = BigInt.asUintN(256, value).toString(16).padStart(64, "0");
return Uint8Array.from(Buffer.from(hex, "hex"));
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

src/utils.ts already has bigIntToBytes. It encodes a bigint as a 32-byte two's-complement word, which is what abiWord does here. Please use it and remove this helper. This also removes the Node-only Buffer from the test. The calls on lines 14 and 20 then become bigIntToBytes(value).

Suggested change
import { bytesToSignedBigInt } from "../src/utils.js";
function abiWord(value: bigint): Uint8Array {
const hex = BigInt.asUintN(256, value).toString(16).padStart(64, "0");
return Uint8Array.from(Buffer.from(hex, "hex"));
}
import { bigIntToBytes, bytesToSignedBigInt } from "../src/utils.js";

Comment thread test/signed-integers.spec.ts Outdated
});
}
}
it("decodes actual negative Uniswap ticks", () => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Non-blocking: these tests cover the helper only. The bug shows in decodeWord in src/calldata.ts. A test through format() with an int24 field would also cover that path. The negative Uniswap ticks from the PR description are a good case for a new folder under test/registry-cases.

…case

- Rename test/signed-integers.spec.ts to test/utils.spec.ts and use bigIntToBytes
- Add Ekubo Positions registry case with sign-extended int32 ticks
- Document test layout rules in AGENTS.md
- Add pad32, padAddr, padInt and padRight32 to test/utils.ts
- Replace the local copies in five test files
- padInt encodes negative values as two's complement
@manuelwedler
manuelwedler merged commit 5281d81 into sourcifyeth:main Sep 10, 2026
2 checks passed
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.

2 participants