feat: bit-packed vault, ERC-1967 Yul proxy, transient-storage transformer, mapping-slot calculator - #755
Merged
mijinummi merged 1 commit intoJul 30, 2026
Conversation
…rmer, mapping-slot calculator ## MDTechLabs#718 — Yul mapping slot calculator (contracts/utils/YulMappingSlot.sol) Computes storage slots for mapping(address => uint256) directly via Yul scratch-space keccak256, matching the compiler's own layout. Verified against a real deployed contract in test/utils/YulMappingSlot.t.sol (Foundry, 8 tests incl. fuzzing over arbitrary addresses/values and a max-uint256 round trip), plus test/utils/YulMappingSlot.test.ts per the issue's stated path. ## MDTechLabs#720 — Minimal ERC-1967 proxy in Yul (contracts/proxy/MinimalERC1967Proxy.sol) Reads the implementation address from the standard ERC-1967 slot (bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1), verified against `cast keccak`) on every call via sload, then delegatecalls and relays return data/revert reason, matching YulProxyForwarder's forwarding mechanics but reading the target from storage instead of an immutable (trading immutability for the ERC-1967-mandated upgrade path). test/proxy/MinimalERC1967Proxy.t.sol (8 tests) verifies slot placement, the Upgraded event, delegatecall executing against the *proxy's* storage, revert-reason forwarding, and that swapping the stored implementation address changes behavior on the next call. ## MDTechLabs#721 — EIP-1153 transient-lock transformer (gasguard-cli/src/transformers/transient_lock.rs) Detects the standard reentrancy-guard idiom (a bool flag checked/set/ cleared entirely within one modifier, never read elsewhere) and rewrites it to EIP-1153 tstore/tload, removing the persistent storage declaration. Only variables used *exclusively* in this exact pattern are converted — one also read anywhere else (e.g. a public getter) is left untouched, verified by a dedicated test. Multiple locks in the same contract get distinct sequential transient slots. 7 unit tests. gasguard-cli/test/fixtures/transient_transform.sol is the sample contract from the issue; its transformed output was independently verified to compile cleanly under solc 0.8.24 --evm-version cancun. ## MDTechLabs#722 — Bit-packed vault accounting engine (contracts/vault/PackedVaultEngine.sol) Packs (balance: uint128, assetId: uint32, lockTimestamp: uint64) into one bytes32 per (user, assetId) position, using Yul mask/shift to splice the balance field without touching the other two. Balance arithmetic itself uses plain checked uint128 Solidity math (reverts on overflow/underflow exactly like an unpacked field would) — only the field-replacement bit-twiddling is done in assembly, since hand-rolling overflow-checked arithmetic in Yul would be strictly riskier than using the compiler's own. test/vault/PackedVaultEngine.t.sol (14 tests) covers deposit/withdraw/lock semantics, independence across asset ids, and fuzzes balance updates plus overflow edge cases (deposit-at-max-uint128 then one more reverts, withdraw-more-than-balance always reverts) as the issue's acceptance criteria ask for. ## Infrastructure note This repo's `forge build`/`forge test` did not work at all before this change: forge-std was never installed (no lib/, no .gitmodules) and ~12 pre-existing contracts fail to compile for reasons unrelated to this PR (EVM-version/solc-version mismatches, syntax errors). Installed forge-std (`forge install foundry-rs/forge-std`, adding .gitmodules, lib/forge-std, foundry.lock, and libs=["lib"] in foundry.toml) since no Foundry test — including the pre-existing tests/gas/GasBenchmarkSuite.t.sol — could run without it. All contracts/tests in this PR were verified with `forge test --use 0.8.24 --evm-version cancun --skip <12 unrelated broken files>`; every skipped file is pre-existing and untouched by this change.
|
@Emmanuelchukwunonso 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! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#718 — Yul mapping slot calculator
contracts/utils/YulMappingSlot.solcomputesmapping(address => uint256)storage slots directly via Yul scratch-spacekeccak256, matching the compiler's own layout. Verified with a real deployed contract intest/utils/YulMappingSlot.t.sol(8 Foundry tests, fuzzed over arbitrary addresses/values including a max-uint256 round trip), plustest/utils/YulMappingSlot.test.tsper the issue's stated path.#720 — Minimal ERC-1967 proxy in Yul
contracts/proxy/MinimalERC1967Proxy.solreads the implementation address from the standard ERC-1967 slot (bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1), cross-checked withcast keccak) viasloadon every call, thendelegatecalls and relays return data/revert reason — same forwarding mechanics as this repo's existingYulProxyForwarder, but reading the target from storage instead of an immutable, trading immutability for the ERC-1967-mandated upgrade path.test/proxy/MinimalERC1967Proxy.t.sol(8 tests) verifies slot placement, theUpgradedevent, delegatecall executing against the proxy's storage (not the implementation's), revert-reason forwarding, and that swapping the stored implementation changes behavior on the next call.#721 — EIP-1153 transient-lock transformer
gasguard-cli/src/transformers/transient_lock.rsdetects the standard reentrancy-guard idiom (aboolflag checked/set/cleared entirely within one modifier, never read elsewhere) and rewrites it totstore/tload, removing the persistent declaration. Only variables used exclusively in this exact pattern convert — one also read elsewhere (e.g. a public getter) is left untouched (dedicated test for this). Multiple locks in the same contract get distinct sequential transient slots. 7 unit tests.gasguard-cli/test/fixtures/transient_transform.solis the sample contract; its transformed output was independently compiled clean undersolc 0.8.24 --evm-version cancun.#722 — Bit-packed vault accounting engine
contracts/vault/PackedVaultEngine.solpacks(balance: uint128, assetId: uint32, lockTimestamp: uint64)into onebytes32per(user, assetId)position, using Yul mask/shift to splice the balance field without disturbing the other two. Balance arithmetic uses plain checkeduint128Solidity math (reverts on overflow/underflow like an unpacked field would) — only the field-replacement bit-twiddling is in assembly, since hand-rolling overflow-checked arithmetic in Yul is strictly riskier than the compiler's own.test/vault/PackedVaultEngine.t.sol(14 tests) covers deposit/withdraw/lock semantics, cross-asset independence, and fuzzes balance updates plus overflow edge cases (deposit-at-max-uint128then one more reverts; withdraw-more-than-balance always reverts).Infra note:
forge build/forge testdidn't work at all onmainforge-stdwas never installed (nolib/, no.gitmodules), so no Foundry test in this repo — including the pre-existingtests/gas/GasBenchmarkSuite.t.sol— could run. Installed it (.gitmodules,lib/forge-std,foundry.lock,libs=["lib"]infoundry.toml). Separately, ~12 pre-existing contracts fail to compile for reasons unrelated to this PR (EVM-version/solc-version mismatches, syntax errors) — every contract/test here was verified withforge test --use 0.8.24 --evm-version cancun --skip <those 12 files>; none of them are touched by this change.Test plan
forge test --use 0.8.24 --evm-version cancun --skip StorageCleaner.sol BatchGuardProcessor.sol UnrolledSignatureVerifier.sol YulMathLib.sol DirectIndexRouter.sol GasGuardRouter.sol GasGuardVault.sol HighVolumeDispatcher.sol YulPermitHandler.sol TransientCache.sol TransientStateSnapshot.sol MappingResolver.sol— 30/30 Solidity tests passing (including fuzzing, 256 runs each)cargo testingasguard-cli— 7/7 new tests passing; 2 pre-existing unrelated failures incommands::optimize_storagereproduce identically onmainwithout this changesolc 0.8.24 --evm-version cancunCloses #718
Closes #720
Closes #721
Closes #722