Skip to content

Compact bitmap storage, Yul mulDiv/safe-transfer libraries, unchecked-loop AST rewriter - #648

Merged
mijinummi merged 2 commits into
MDTechLabs:mainfrom
ogazboiz:feat/issues-632-638-639-640
Jul 28, 2026
Merged

Compact bitmap storage, Yul mulDiv/safe-transfer libraries, unchecked-loop AST rewriter#648
mijinummi merged 2 commits into
MDTechLabs:mainfrom
ogazboiz:feat/issues-632-638-639-640

Conversation

@ogazboiz

Copy link
Copy Markdown
Contributor

Changes

  • [PERF] Replace Mapping(address => bool) with Compact Bitmap Storage #632 — Compact bitmap storage: contracts/data/BitmapTracker.sol packs 256 boolean flags per storage slot (mapping(uint256 => uint256) buckets, index >> 8 selects the slot / index & 0xff selects the bit) instead of one 32-byte slot per flag. isSet/set/unset on a generic bitmap, plus three named namespaces (operational flags, processed nonces, user claims) sharing the same bit-shift helpers so they can't collide on the same index.
  • [CORE] Implement Zero-Overhead Yul Fixed-Point Math Library #638 — Zero-overhead Yul fixed-point math: contracts/math/YulMathLib.sol's mulDivDown(x, y, denominator) is implemented entirely in Yul, re-deriving the standard 512-bit-safe mulDiv technique (exact product via mulmod/CRT reconstruction, exact-remainder subtraction, power-of-two factoring, 6-step Newton-Raphson modular inverse) so x * y can exceed 256 bits without reverting early, as long as the final quotient still fits. Reverts with DivisionByZero/MulDivOverflow custom errors instead of a generic Solidity panic.
  • [CORE] Build Low-Level Assembly Call Forwarder for Non-Standard Tokens #639 — Low-level token transfer forwarder: contracts/tokens/YulSafeTransfer.sol builds ERC20 transfer/transferFrom calldata by hand (mstore into scratch memory) and executes a raw call, branching on returndatasize() to correctly accept standard bool-returning tokens, non-standard tokens that return nothing at all (USDT-style), and to bubble up the original revert reason on failure — rejecting only a literal false return or an unexpected data length.
  • [CORE] Rule G005: Automated AST Rewriter for Unchecked Loop Counters #640 — Rule G005, unchecked-loop AST rewriter: packages/cli/src/transformers/loop_unchecked.rs (the gasguard-cli crate) scans Solidity source (comment/string-aware, so it never mistakes text inside a ////* *//string literal for real code) and rewrites for loops whose counter is declared inline and stepped by a simple i++/++i/i += 1 into the unchecked { ++i; } idiom — preserving indentation and comments, and correctly handling nested loops. Deliberately leaves already-unchecked, decrementing, differently-stepped, and externally-declared counters untouched.

Also fixed (found while implementing #632/#638/#639)

apps/api-service/hardhat.config.ts pinned @nomicfoundation/hardhat-toolbox@^6.1.0, which only supports Hardhat 2, alongside hardhat@^3.1.9 — a broken combination (Cannot find module '@nomicfoundation/hardhat-chai-matchers') that made npx hardhat compile/test completely unusable, blocking these first-ever Solidity contracts and tests in this app. Swapped to the Hardhat-3-native hardhat-toolbox-mocha-ethers, its peer plugins, and the network type discriminators Hardhat 3 requires.

Verified this doesn't regress the existing NestJS/Jest suite: its Cannot find module 'jest-util' failure reproduces identically on a from-scratch reinstall of the original, untouched package.json (a pre-existing --legacy-peer-deps dependency-hoisting issue, unrelated to this change) — confirmed independently, not just via the branch's own state.

Test plan

  • npx hardhat compile (from apps/api-service/) — clean, solc 0.8.20
  • npx hardhat test test/data/BitmapTracker.test.ts test/math/YulMathLib.test.ts test/tokens/YulSafeTransfer.test.ts26/26 passing, including a 250-case fuzz run for mulDivDown against a BigInt reference implementation and full USDT/reverting/false-returning token coverage for YulSafeTransfer
  • cargo test -p gasguard-cli12/12 passing, including nested-loop and comment/string-literal edge cases; independently confirmed the rewritten fixture output compiles cleanly via solc
  • cargo clippy -p gasguard-cli --tests / cargo fmt -p gasguard-cli -- --check — clean
  • Every Yul assembly block hand-verified line-by-line against the well-established algorithms it re-derives (Uniswap FullMath-style 512-bit mulDiv; the standard USDT-tolerant safe-transfer branching)

Closes #632
Closes #638
Closes #639
Closes #640

…hecked-loop AST rewriter

- BitmapTracker (contracts/data/): packs 256 boolean flags per storage slot
  via mapping(uint256 => uint256) buckets instead of one 32-byte slot per
  flag, cutting a fresh-flag SSTORE from ~20,000 gas down to a warm-slot
  write once a bucket has been touched once. isSet/set/unset plus three
  named namespaces (operational flags, processed nonces, user claims)
  sharing the same bit-shift helpers.
- YulMathLib (contracts/math/): mulDivDown(x, y, denominator) entirely in
  Yul, re-deriving the standard 512-bit-safe mulDiv technique (CRT-based
  exact product via mulmod, exact-remainder subtraction, power-of-two
  factoring, Newton-Raphson modular inverse) so x*y can exceed 256 bits
  without reverting early, as long as the final quotient fits. Reverts
  with custom errors (DivisionByZero, MulDivOverflow) instead of a
  generic panic.
- YulSafeTransfer (contracts/tokens/): hand-built ERC20 transfer/
  transferFrom calldata via mstore + raw call, branching on returndata to
  accept standard (bool-returning), non-standard (USDT-style, no return
  value), and reverting tokens correctly, rejecting only a literal
  `false` return or an unexpected data length.
- Rule G005 (packages/cli/src/transformers/loop_unchecked.rs): a
  comment/string-aware source scanner that rewrites `for` loops whose
  counter is declared inline and stepped by a simple `i++`/`++i`/`i += 1`
  into the `unchecked { ++i; }` idiom, preserving indentation and leaving
  already-unchecked, decrementing, differently-stepped, and externally-
  declared counters untouched.

Also fixes apps/api-service/hardhat.config.ts and package.json: the
pinned @nomicfoundation/hardhat-toolbox@^6.1.0 only supports Hardhat 2,
paired here with hardhat@^3.1.9 — a broken combination that made
`npx hardhat compile`/`test` unusable for these first-ever Solidity
contracts in this app. Swapped to the Hardhat-3-native
hardhat-toolbox-mocha-ethers, its peer plugins, and the network `type`
discriminators Hardhat 3 requires. Confirmed this does not regress the
existing NestJS/Jest suite: its `Cannot find module 'jest-util'` failure
reproduces identically on a clean reinstall of the original, untouched
package.json (a pre-existing --legacy-peer-deps dependency-hoisting
issue, unrelated to this change).

Closes MDTechLabs#632
Closes MDTechLabs#638
Closes MDTechLabs#639
Closes MDTechLabs#640
@drips-wave

drips-wave Bot commented Jul 28, 2026

Copy link
Copy Markdown

@ogazboiz Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@mijinummi

Copy link
Copy Markdown
Collaborator

please resolve conflict @ogazboiz

@mijinummi
mijinummi merged commit 9c9b3ab into MDTechLabs:main Jul 28, 2026
3 of 7 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

3 participants