Skip to content

feat(contracts): add Uint256 and Vector32 modules, remove archived Uint256, part 2/3 - #291

Merged
0xisk merged 17 commits into
mainfrom
refactor/upgrade-math-0.28.0-part2
Feb 23, 2026
Merged

feat(contracts): add Uint256 and Vector32 modules, remove archived Uint256, part 2/3#291
0xisk merged 17 commits into
mainfrom
refactor/upgrade-math-0.28.0-part2

Conversation

@0xisk

@0xisk 0xisk commented Feb 9, 2026

Copy link
Copy Markdown
Member

Types of changes

What types of changes does your code introduce to OpenZeppelin Midnight Contracts?
Put an x in the boxes that apply

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation Update (if none of the other choices apply)

Part of #279

Introduce the U256 struct foundation and 32-element vector conversions:

  • Uint256: 256-bit unsigned integer arithmetic using U256 struct
  • Vector32: conversions from Vector<32, Uint<8>> to U256/Bytes<32>
  • Remove archived Uint256/u256 files superseded by new modules

PR Checklist

  • I have read the Contributing Guide
  • I have added tests that prove my fix is effective or that my feature works
  • I have added documentation of new methods and any new behavior or changes to existing behavior
  • CI Workflows Are Passing

Further comments

If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc...

Summary by CodeRabbit

Release Notes

  • New Features

    • Added 256-bit unsigned integer utilities with comparison and serialization operations.
    • Introduced vector conversion utilities for 256-bit value handling.
  • Chores

    • Migrated archived 256-bit utilities to active codebase.
    • Enhanced test infrastructure with comprehensive coverage for new utilities.
    • Updated code formatting and import organization across multiple modules.
    • Adjusted build tool configuration for format checking.

@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This PR removes archived Uint256 utilities and introduces new, active implementations of Uint256 and Vector32 modules with comparison and conversion circuits. It updates the format script configuration, adds comprehensive test suites and mock simulators, and applies various formatting refinements across the codebase.

Changes

Cohort / File(s) Summary
Build Configuration
contracts/package.json
Updated compact:fmt:fix script to perform format check instead of write/modify operation.
Archive Cleanup
contracts/src/archive/*, contracts/src/archive/test/*, contracts/src/archive/utils/*
Removed deprecated Uint256 module, test suites, mock implementations, simulator utilities, and witness helpers from archive directory (1074 + 1204 + 156 + 122 + 233 + 179 + 37 lines deleted).
AccessControl Updates
contracts/src/access/AccessControl.compact, contracts/src/access/test/mocks/contracts/AccessControl.mock.compact
Formatting adjustments to public API signatures in AccessControl; introduced constructor(initialAdmin) and testGrantRole(user, role) public circuits in mock contract.
Uint256 Implementation
contracts/src/math/Uint256.compact, contracts/src/math/test/Uint256.test.ts, contracts/src/math/test/mocks/Uint256Simulator.ts, contracts/src/math/test/mocks/contracts/Uint256.mock.compact
Added new Uint256 module with circuits for zero/max constants, byte vector conversions, and comparison operations (eq, lt, lte, gt, gte); includes comprehensive test suite with boundary cases and full simulator/mock infrastructure.
Vector32 Implementation
contracts/src/math/Vector32.compact, contracts/src/math/test/Vector32.test.ts, contracts/src/math/test/mocks/Vector32Simulator.ts, contracts/src/math/test/mocks/contracts/Vector32.mock.compact
Introduced Vector32 module for converting 32-byte little-endian vectors to U256 and Bytes<32>; includes test suite validating conversion integrity and round-trip consistency with simulator/mock support.
Minor Uint Utilities
contracts/src/math/Uint128.compact, contracts/src/math/Vector8.compact
Whitespace/indentation adjustments in subtraction logic (Uint128) and byte-packing expression (Vector8); no behavioral changes.
Test Enhancements
contracts/src/math/test/Uint64.test.ts
Added duplicate AddChecked test suite block with identical test cases for additional coverage validation.
Formatting Refinements
contracts/src/shielded-token/..., contracts/src/structs/...
Multi-line import/parameter list reformatting in ShieldedFungibleToken, ShieldedERC20, Utils, and Queue modules; re-exported ShieldedFungibleToken types; no functional changes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • andrew-fleming
  • emnul

Poem

🐰 Hopping through archives, we've cleaned up the past,
Old Uint256 relics—no longer held fast!
Now Uint256 and Vector32 shine ever bright,
With circuits comparing and converting just right,
Our tests multiply like carrots in spring,
What a hoppy refactor of mathematical things!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately describes the main changes: adding Uint256 and Vector32 modules, removing archived Uint256, and indicating this is part 2 of 3.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/upgrade-math-0.28.0-part2

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

@0xisk 0xisk changed the title feat(contracts): add Uint256 and Vector32 modules, remove archived Uint256 feat(contracts): add Uint256 and Vector32 modules, remove archived Uint256, part 2/3 Feb 9, 2026
Base automatically changed from refactor/upgrade-math-0.28.0-part1 to main February 11, 2026 13:52
@0xisk
0xisk force-pushed the refactor/upgrade-math-0.28.0-part2 branch from 01485c0 to 97bf030 Compare February 11, 2026 13:58
@0xisk 0xisk self-assigned this Feb 11, 2026
@0xisk
0xisk marked this pull request as ready for review February 11, 2026 14:01
@0xisk
0xisk requested review from a team as code owners February 11, 2026 14:01

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@contracts/src/math/test/Uint64.test.ts`:
- Around line 139-170: The test file contains a duplicate describe('AddChecked',
...) block that repeats the same six tests against uint64Simulator.addChecked
and MAX_UINT64; remove the second (duplicate) describe('AddChecked', ...) block
so only one AddChecked suite remains to avoid redundant tests and duplicate test
names.
🧹 Nitpick comments (4)
contracts/src/math/Uint256.compact (2)

47-62: Inconsistent pure modifier between ZERO_U256 and MAX_U256.

ZERO_U256 (line 47) is not marked as pure, while MAX_U256 (line 60) is marked as pure. Both circuits only perform struct construction with constant values and have no side effects. Consider adding the pure modifier to ZERO_U256 for consistency and potential optimization.

♻️ Proposed fix
-  export circuit ZERO_U256(): U256 {
+  export pure circuit ZERO_U256(): U256 {
     return U256 { low: U128 { low: 0, high: 0 }, high: U128 { low: 0, high: 0 } };
   }

213-215: Consider optimizing lte and gte to avoid redundant comparisons.

Both lte and gte call their respective lt/gt plus eq. Since lt and gt already perform partial equality checks internally (via Uint128_eqU128 for high parts), there may be circuit-level redundancy. However, the current implementation is straightforward and correct.

If circuit row count becomes a concern, you could implement lte as !gt(a, b) and gte as !lt(a, b), which avoids calling eq separately. This is a minor optimization suggestion.

Also applies to: 261-263

contracts/src/math/test/mocks/contracts/Vector32.mock.compact (1)

17-25: Inconsistent use of disclose() between toU256 and toBytes.

toU256 (line 19) wraps the result with disclose(), but toBytes (line 24) does not. In the source Vector32.compact, toU256 already uses disclose() internally (line 119), so the mock is adding a redundant disclose(). Meanwhile, toBytes in the source does not use disclose().

Consider either:

  1. Removing the redundant disclose() from toU256 in the mock, or
  2. Adding disclose() to toBytes if the intent is to expose all outputs for circuit metadata reporting.
contracts/src/math/test/mocks/contracts/Uint256.mock.compact (1)

4-4: Inconsistent pragma version: 0.18.0 vs 0.20.0 in other files.

This file uses pragma language_version >= 0.18.0 (line 4), while the production modules (Uint256.compact, Vector32.compact) and the other mock (Vector32.mock.compact) use pragma language_version >= 0.20.0. Consider updating to 0.20.0 for consistency.

♻️ Proposed fix
-pragma language_version >= 0.18.0;
+pragma language_version >= 0.20.0;

Comment thread contracts/src/math/test/Uint64.test.ts

@andrew-fleming andrew-fleming left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@0xisk looks really good! I left a few minor comments and suggestions :)

Comment thread contracts/src/math/test/mocks/contracts/Uint256.mock.compact Outdated
Comment thread contracts/src/math/test/mocks/contracts/Vector32.mock.compact Outdated
Comment thread contracts/src/math/Uint256.compact Outdated
Comment thread contracts/src/math/Uint256.compact Outdated
Comment thread contracts/src/math/Vector32.compact Outdated
Comment thread contracts/src/math/Vector8.compact Outdated
Comment thread contracts/src/math/test/mocks/contracts/Uint256.mock.compact
Comment thread contracts/src/math/Uint256.compact Outdated
Comment thread contracts/src/math/test/mocks/Uint256Simulator.ts Outdated
Comment thread contracts/src/math/test/Uint64.test.ts
0xisk and others added 5 commits February 16, 2026 13:57
Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com>
Signed-off-by: 0xisk <0xisk@proton.me>
Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com>
Signed-off-by: 0xisk <0xisk@proton.me>
@0xisk
0xisk requested a review from andrew-fleming February 16, 2026 13:03

@andrew-fleming andrew-fleming left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Almost there! Left a suggestion and a suggestion/question

Comment thread contracts/src/math/test/mocks/contracts/Vector32.mock.compact Outdated
Comment thread contracts/src/math/Bytes8.compact Outdated
Comment on lines +41 to +50
export pure circuit toUint64(
b0: Uint<8>,
b1: Uint<8>,
b2: Uint<8>,
b3: Uint<8>,
b4: Uint<8>,
b5: Uint<8>,
b6: Uint<8>,
b7: Uint<8>
): Uint<64> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good call with abstracting this out! I'm wondering if it's better to keep this separate or if we can just add this to Vector8 because there's this similar circuit

  export pure circuit toUint64(vec: Vector<8, Uint<8>>): Uint<64> {
    const [b0, b1, b2, b3, b4, b5, b6, b7] = vec;

    return b0 +
           b1 * 0x100 +
           b2 * 0x10000 +
           b3 * 0x1000000 +
           b4 * 0x100000000 +
           b5 * 0x10000000000 +
           b6 * 0x1000000000000 +
           b7 * 0x100000000000000;
  }

What do you think about overloading the toUint64 circuit name so we can have a single module that offers both? They're doing the same thing, they just have different sigs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If you like the idea, I'd probably change the module name to something more generic though...Pack64 or something. lmk what you think

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

That's a great point! I did those changes following on that:

  1. Module Consolidation

    • Removed Vector8.compact and Vector32.compact modules
    • Renamed Vector32 → Bytes32 with expanded functionality
    • Enhanced Bytes8 with pack/unpack operations (previously only had toUint64)
  2. Naming Convention Standardization

    • BytesN modules: Use pack(vec) and unpack(bytes) for Vector ↔ Bytes conversions
    • UintN modules: Use toBytes(value) for packed bytes and toUnpackedBytes(value) for vector representation
    • Renamed Uint64_toVectorUint64_toUnpackedBytes for clarity
    • Renamed Uint256_toVectorUint256_.toUnpackedBytes for consistency
  3. Circuit Additions

    • Bytes8: Added pack(), unpack(), and overloaded toUint64() circuits
    • Bytes32: Added pack(), unpack(), and overloaded toU256() circuits
    • Both modules now support conversions from both Vector and Bytes inputs

@andrew-fleming andrew-fleming left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Tiny tidy up details and we're good to go! The main thing is the bad witness tests

Comment thread contracts/src/math/Bytes32.compact
Comment thread contracts/src/math/test/mocks/contracts/Bytes32.mock.compact Outdated
Comment thread contracts/src/math/test/mocks/contracts/Bytes8.mock.compact Outdated
Comment thread contracts/src/math/test/mocks/contracts/Uint256.mock.compact Outdated
Comment thread contracts/src/math/test/Bytes32.test.ts
Comment thread contracts/src/math/test/Bytes8.test.ts
Comment thread contracts/src/math/test/Uint64.test.ts
Comment thread contracts/src/math/test/Uint256.test.ts
Comment thread contracts/src/math/test/Bytes32.test.ts Outdated
Comment thread contracts/src/math/test/Bytes8.test.ts Outdated
@0xisk 0xisk linked an issue Feb 20, 2026 that may be closed by this pull request
0xisk and others added 4 commits February 20, 2026 10:35
Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com>
Signed-off-by: 0xisk <0xisk@proton.me>
Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com>
Signed-off-by: 0xisk <0xisk@proton.me>
Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com>
Signed-off-by: 0xisk <0xisk@proton.me>
@0xisk

0xisk commented Feb 20, 2026

Copy link
Copy Markdown
Member Author

@andrew-fleming thank you for the review. I covered all the missing cases for tests. Also took the chance to refactor Uint128 module circuits with overloading. Let me know what do you think? and I was thinking to rename circuits toU128 and toUint128tounpackandpack` as technically those two are for the same value and type (integers) but using U128 struct, similar to how in Bytes32 and Bytes8. Wdyt?

@0xisk
0xisk requested a review from andrew-fleming February 20, 2026 12:09

@andrew-fleming andrew-fleming left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM @0xisk! I left a copy tiny suggestions, but we can address them in another PR to not stall this any further if you'd like. Happy to re-approve if not

Comment on lines +76 to +86
test('bytesToUint64 should fail when witness returns pack(vec) != bytes', () => {
bytes8Simulator.overrideWitness('wit_unpackBytes8', (context, _bytes) => [
context.privateState,
bytes(0, 0, 0, 0, 0, 0, 0, 0),
]);
const packed = new Uint8Array(8);
packed[0] = 1;
expect(() => bytes8Simulator.bytesToUint64(packed)).toThrow(
'failed assert: Bytes8: unpack verification failed',
);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: I'd move this to the bytesToUint64 block

Comment on lines +97 to +109
test('bytesToU256 should fail when witness returns pack(vec) != bytes', () => {
bytes32Simulator.overrideWitness(
'wit_unpackBytes32',
(context, _bytes) => [
context.privateState,
new Array<bigint>(32).fill(0n),
],
);
const bytes = new Uint8Array(32);
bytes[0] = 1;
expect(() => bytes32Simulator.bytesToU256(bytes)).toThrow(
'failed assert: Bytes32: unpack verification failed',
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'd move this to the bytesToU256 block

Comment on lines +988 to +1002
test('should fail when witness returns remainder >= divisor', () => {
uint128Simulator.overrideWitness('wit_divU128', (context) => [
context.privateState,
{ quotient: { low: 1n, high: 0n }, remainder: { low: 5n, high: 0n } },
]);
expect(() => uint128Simulator.divRemU128(u128(10n), u128(5n))).toThrow(
'failed assert: Uint128: remainder error',
);
});

test('should fail when witness returns quotient * b + remainder != a', () => {
uint128Simulator.overrideWitness('wit_divU128', (context) => [
context.privateState,
{ quotient: { low: 2n, high: 0n }, remainder: { low: 0n, high: 0n } },
]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

👍

@0xisk

0xisk commented Feb 23, 2026

Copy link
Copy Markdown
Member Author

LGTM @0xisk! I left a copy tiny suggestions, but we can address them in another PR to not stall this any further if you'd like. Happy to re-approve if not

Thank you @andrew-fleming! I will address this in the 3rd part.

I was thinking to rename circuits toU128 and toUint128tounpackandpack` as technically those two are for the same value and type (integers) but using U128 struct, similar to how in Bytes32 and Bytes8. Wdyt?

Also @andrew-fleming let me know wdyt here?

@0xisk
0xisk merged commit 60ad96a into main Feb 23, 2026
12 checks passed
@0xisk
0xisk deleted the refactor/upgrade-math-0.28.0-part2 branch February 23, 2026 08:55
@coderabbitai coderabbitai Bot mentioned this pull request Mar 9, 2026
8 tasks
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/2] Math modules v0.29.0

2 participants