Skip to content

feat(LibBytes): add address<->bytes32 conversion helpers (EXSC-618) [LibBytes v1.1.0]#2055

Open
0xDEnYO wants to merge 3 commits into
mainfrom
feature/exsc-618-sc-libbytes-v110-addressbytes32-conversion-helpers
Open

feat(LibBytes): add address<->bytes32 conversion helpers (EXSC-618) [LibBytes v1.1.0]#2055
0xDEnYO wants to merge 3 commits into
mainfrom
feature/exsc-618-sc-libbytes-v110-addressbytes32-conversion-helpers

Conversation

@0xDEnYO

@0xDEnYO 0xDEnYO commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Which Linear task belongs to this PR?

EXSC-618

Why did I implement it this way?

The bytes32(uint256(uint160(addr))) / address(uint160(uint256(b))) conversion pattern is copy-pasted across ~10 facets, which triggers Olympix unsafe-downcast findings and risks divergent semantics. The only prior implementation lived in the vendored OFTComposeMsgCodec (a LayerZero copy), so it was not a suitable shared dependency.

LibBytes v1.1.0 now centralises it:

  • toBytes32(address) — widening, always lossless.
  • toAddress(bytes32)checked narrowing: reverts NotAnAddress if the top 96 bits are set. This is the default; a checked narrowing silences the detector honestly rather than hiding a truncation. New overload alongside the existing toAddress(bytes memory, uint256).
  • toAddressUnchecked(bytes32) — raw truncation, for intentional / pre-guarded cases.
  • Drive-by: toHexString's require-string → custom error HexLengthInsufficient (removes the gas-custom-errors solhint-disable).
  • Unit tests: round-trip, zero, checked-revert, unchecked-truncation.

Note on the toHexString drive-by (transitive bytecode change): toHexString is internal pure and inlined into its sole caller SquidFacet (SquidFacet.sol:187), so swapping the require-string for revert HexLengthInsufficient() changes SquidFacet's compiled bytecode and revert-encoding even though SquidFacet.sol isn't edited here. The affected branch is unreachable for a correctly-sized length (after the loop value is always 0, and SquidFacet always passes a correct length), so there is no reachable behavior change — hence no @custom:version bump or re-audit for SquidFacet (per [CONV:VERSIONING] this is a PATCH-class change that does not cascade). Flagged so whoever next deploys SquidFacet knows its 1.0.0 bytecode will differ from the currently-deployed 1.0.0.

Call sites are intentionally NOT migrated in this PR. Each of the 21 existing sites gets a breadcrumb comment only (// TODO(EXSC-626): migrate to LibBytes.…, tracked in EXSC-626) — no logic change, no facet @custom:version bump, no re-audit. Each is swapped to the helper the next time its facet is touched. Audit of the sites:

  • 9 wideningtoBytes32 (zero behavior change).
  • 6 checked narrowingtoAddress: all are validation/compare paths that already revert on a bad value, so checked only changes which revert fires — never turns a revert into success.
  • 6 → toAddressUnchecked: CalldataVerificationFacet (a view returning bool — checked would turn a legitimate return false into a revert) and the AcrossFacetPackedV4 packed hot paths (two of which already manually guard the high bits before branching to NON_EVM).

Numeric downcasts (uintN — the larger source of findings) are tracked separately in EXSC-617 (SafeCast + a [CONV:DOWNCAST] rule).

Checklist before requesting a review

Checklist for reviewer (DO NOT DEPLOY and contracts BEFORE CHECKING THIS!!!)

  • I have checked that any arbitrary calls to external contracts are validated and or restricted
  • I have checked that any privileged calls (i.e. storage modifications) are validated and or restricted
  • I have ensured that any new contracts have had AT A MINIMUM 1 preliminary audit conducted on by <company/auditor>

Consolidate the address<->bytes32 cast pattern duplicated across ~10 facets
into LibBytes (v1.1.0):
- toBytes32(address): widening, lossless
- toAddress(bytes32): checked narrowing, reverts NotAnAddress on dirty high bits
- toAddressUnchecked(bytes32): raw truncation for intentional/guarded cases
Also replace toHexString's require-string with a custom error. Add unit tests.

Leave breadcrumb comments at the 21 existing call sites (checked/unchecked per
audit); each is migrated the next time its facet is touched, so this PR makes
no facet behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@lifi-action-bot
lifi-action-bot marked this pull request as draft July 15, 2026 03:55
@github-actions github-actions Bot added the requires-types Trigger Types Bindings CI (ABI/type generation for lifi-contract-types) label Jul 15, 2026
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

LibBytes now provides checked and unchecked bytes32/address conversions and custom errors. Tests cover the new behavior, while bridge facets and ReceiverOIF include TODO markers for future migration to these helpers.

Changes

LibBytes conversion utilities

Layer / File(s) Summary
Library errors and conversions
src/Libraries/LibBytes.sol
Adds toBytes32, checked toAddress, unchecked toAddressUnchecked, and custom validation errors.
Conversion helper tests
test/solidity/Libraries/LibBytes.t.sol
Adds harness wrappers and tests for padding, round trips, validation, truncation, and custom errors.
Bridge migration markers
src/Facets/*, src/Periphery/ReceiverOIF.sol, .agents/rules/100-solidity-basics.md
Adds TODO comments identifying existing address and bytes32 conversion sites for future LibBytes migration and documents the required helper usage.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: adding LibBytes address/bytes32 conversion helpers and the version bump.
Description check ✅ Passed The description follows the required template and includes the task, rationale, and both checklists with the key details filled in.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/exsc-618-sc-libbytes-v110-addressbytes32-conversion-helpers

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.

@lifi-action-bot lifi-action-bot changed the title feat(LibBytes): add address<->bytes32 conversion helpers (EXSC-618) feat(LibBytes): add address<->bytes32 conversion helpers (EXSC-618) [LibBytes v1.1.0] Jul 15, 2026
Comment thread src/Libraries/LibBytes.sol Dismissed
Comment thread src/Libraries/LibBytes.sol Dismissed
Comment thread src/Libraries/LibBytes.sol Dismissed
@0xDEnYO
0xDEnYO marked this pull request as ready for review July 15, 2026 03:59
@lifi-action-bot

lifi-action-bot commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

🤖 GitHub Action: Security Alerts Review 🔍

🟢 Dismissed Security Alerts with Comments
The following alerts were dismissed with proper comments:

🟢 View Alert - File: src/Libraries/LibBytes.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: LibBytes.toAddressUnchecked: intentional truncation helper (keeps low 160 bits by design); used only where dropping high bits is deliberate/pre-guarded. Centralised helper (EXSC-618).

🟢 View Alert - File: src/Libraries/LibBytes.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: LibBytes.toAddress: checked narrowing — reverts NotAnAddress if the top 96 bits are set (guard on the preceding line), so it cannot silently truncate. Centralised helper (EXSC-618).

🟢 View Alert - File: src/Libraries/LibBytes.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: LibBytes.toBytes32: widening address->bytes32 (160->256 bits), lossless — no truncation possible. Centralised helper (EXSC-618).

🟢 View Alert - File: src/Facets/AcrossFacetPackedV4.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Intentional truncation in the gas-optimized packed facet (receiver casts are pre-guarded by a bytes12 zero-check before branching to NON_EVM). Reminder: migrate to LibBytes.toAddressUnchecked when this facet is next touched — EXSC-618 / LibBytes v1.1.0.

🟢 View Alert - File: src/Facets/AcrossFacetPackedV4.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Intentional truncation in the gas-optimized packed facet (receiver casts are pre-guarded by a bytes12 zero-check before branching to NON_EVM). Reminder: migrate to LibBytes.toAddressUnchecked when this facet is next touched — EXSC-618 / LibBytes v1.1.0.

🟢 View Alert - File: src/Facets/AcrossFacetPackedV4.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Intentional truncation in the gas-optimized packed facet (receiver casts are pre-guarded by a bytes12 zero-check before branching to NON_EVM). Reminder: migrate to LibBytes.toAddressUnchecked when this facet is next touched — EXSC-618 / LibBytes v1.1.0.

🟢 View Alert - File: src/Facets/AcrossFacetPackedV4.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Intentional truncation in the gas-optimized packed facet (receiver casts are pre-guarded by a bytes12 zero-check before branching to NON_EVM). Reminder: migrate to LibBytes.toAddressUnchecked when this facet is next touched — EXSC-618 / LibBytes v1.1.0.

🟢 View Alert - File: src/Facets/AcrossFacetPackedV4.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Intentional truncation in the gas-optimized packed facet (receiver casts are pre-guarded by a bytes12 zero-check before branching to NON_EVM). Reminder: migrate to LibBytes.toAddressUnchecked when this facet is next touched — EXSC-618 / LibBytes v1.1.0.

🟢 View Alert - File: src/Facets/LiFiIntentEscrowFacetV2.sol
🔹 Contracts that can receive ether but cannot send it may lock value permanently. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/locked-ether
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: This is a facet, not a stand-alone contract. This contract will be delegated called by the diamond and the diamond is responsible for managing native eth.

🟢 View Alert - File: src/Facets/LiFiIntentEscrowFacetV2.sol
🔹 Reentrant functions which emit events after making an external call may lead to out-of-order events. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/reentrancy-events
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Follows best practises from other facets.

🟢 View Alert - File: src/Facets/AcrossV4SwapFacet.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: narrow casting is safe here

🟢 View Alert - File: src/Facets/AcrossV4SwapFacet.sol
🔹 Reentrant functions which emit events after making an external call may lead to out-of-order events. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/reentrancy-events
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: acknowledged. Since we do not keep any state, we can emit events before external calls

🟢 View Alert - File: src/Facets/AcrossV4SwapFacet.sol
🔹 Parameters passed to a constructor that are not validated for correct values may lead to contract creation in an undesired state. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/no-parameter-validation-in-constructor
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: some of these might be address(0) by design, we cannot validate further

🟢 View Alert - File: src/Facets/LiFiIntentEscrowFacet.sol
🔹 Contracts that can receive ether but cannot send it may lock value permanently. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/locked-ether
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: Widening address->bytes32 (160->256 bits), lossless — no truncation possible. Reminder: migrate to LibBytes.toBytes32 when this facet is next touched — EXSC-618 / LibBytes v1.1.0.

🟢 View Alert - File: src/Facets/AcrossV4SwapFacet.sol
🔹 Parameters passed to a constructor that are not validated for correct values may lead to contract creation in an undesired state. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/no-parameter-validation-in-constructor
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: some of these parameters can be address(0) by design, we cannot validate further here

🟢 View Alert - File: src/Facets/AcrossV4SwapFacet.sol
🔹 Performing integer division before multiplication can lead to unnecessary loss of precision. Please note: this detector flags the division, the associated multiplication may be remote! Check function and variable chains to confirm. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/faulty-div
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: we are multiplying first...

🟢 View Alert - File: src/Facets/AcrossV4SwapFacet.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: this is the standard solidity idiom for recovering an address from bytes32

🟢 View Alert - File: src/Facets/AcrossV4SwapFacet.sol
🔹 Reentrant functions which emit events after making an external call may lead to out-of-order events. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/reentrancy-events
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: nonReentrant is applied on both public entrypoints

🟢 View Alert - File: src/Facets/AcrossV4SwapFacet.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: This is fine

🟢 View Alert - File: src/Facets/CelerCircleBridgeFacet.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: This is an upcast. No data is lost. It produces the correct compliant format - left padded

🟢 View Alert - File: src/Facets/NEARIntentsFacet.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Intentional

🟢 View Alert - File: src/Facets/NEARIntentsFacet.sol
🔹 Contracts that can receive ether but cannot send it may lock value permanently. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/locked-ether
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: This is a facet and part of a proxy which is configured correctly

🟢 View Alert - File: src/Periphery/ReceiverOIF.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: On an EVM chain, the value will always be equal to 20 bytes.

🟢 View Alert - File: src/Periphery/ReceiverOIF.sol
🔹 Using a payable fallback (including receive) with no explicit functionality may indicate incomplete contract logic. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/empty-payable-fallback
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: This is required functionality.

🟢 View Alert - File: src/Facets/PolymerCCTPFacet.sol
🔹 Performing a narrowing downcast may result in silent overflow due to bit truncation. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unsafe-downcast
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: known and accepted

🟢 View Alert - File: src/Facets/PolymerCCTPFacet.sol
🔹 Contracts that can receive ether but cannot send it may lock value permanently. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/locked-ether
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: It's a facet that is part of our diamond which does have withdrawal capabilities

🟢 View Alert - File: src/Facets/GlacisFacet.sol
🔹 External calls to functions with dynamic return types may possibly run out of gas if calling a malicious function. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/external-call-potential-out-of-gas
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Not an issue

🟢 View Alert - File: src/Facets/GlacisFacet.sol
🔹 Calling a function without checking the return value may lead to silent failures. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/unused-return-function-call
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Not an issue

🟢 View Alert - File: src/Facets/PolymerCCTPFacet.sol
🔹 Reentrant functions which emit events after making an external call may lead to out-of-order events. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/reentrancy-events
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: established pattern, safe since we do not update any state and events are more for informational/analytics purpose (order does not matter)

🟢 View Alert - File: src/Facets/LiFiIntentEscrowFacet.sol
🔹 Reentrant functions which emit events after making an external call may lead to out-of-order events. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/reentrancy-events
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: This is intended

No unresolved security alerts! 🎉

@lifi-action-bot
lifi-action-bot marked this pull request as draft July 15, 2026 03:59
@lifi-action-bot

lifi-action-bot commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Test Coverage Report

Line Coverage: 90.23% (3483 / 3860 lines)
Function Coverage: 93.90% ( 539 / 574 functions)
Branch Coverage: 73.43% ( 644 / 877 branches)
Test coverage (90.23%) is above min threshold (89%). Check passed.

…elpers (EXSC-618)

New convention so future facets use LibBytes.toBytes32 / toAddress (checked) /
toAddressUnchecked instead of hand-rolling address<->bytes32 casts — which keeps
call sites free of the Olympix unsafe-downcast finding (centralised in LibBytes).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@0xDEnYO
0xDEnYO marked this pull request as ready for review July 15, 2026 05:25
@0xDEnYO
0xDEnYO enabled auto-merge (squash) July 15, 2026 05:30
}
// solhint-disable-next-line gas-custom-errors
require(value == 0, "Strings: hex length insufficient");
if (value != 0) revert HexLengthInsufficient();

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.

toHexString change silently alters SquidFacet bytecode with no version bump.

LibBytes functions are internal pure and get inlined into consumers. SquidFacet.sol:187 is the sole caller of toHexString, so swapping the require-string for revert HexLengthInsufficient() changes SquidFacet's compiled bytecode and its revert data — yet SquidFacet stays at @custom:version 1.0.0 and isn't listed among the "call sites intentionally not migrated". The PR body implies zero facet behavior changes, but this one does change.

Per [CONV:VERSIONING], "new revert on existing function" is a MAJOR-type change, and this is a revert-encoding change on a shipped function. In practice the branch is an unreachable invariant (after the loop value is always 0 for a correctly-sized input), so behavioral/security risk is effectively nil — but it should be surfaced rather than folded silently into a "no facets touched" PR.

Suggestion: either split the toHexString refactor into its own PR acknowledging the SquidFacet impact, or note in the PR body that SquidFacet's bytecode changes and confirm with audit/deploy that an unbumped version is acceptable here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not bumping SquidFacet's version here — but you're right that the transitive bytecode change should be surfaced, so I've added a note to the PR description.

Reasoning: [CONV:VERSIONING] says a library MAJOR bump should be evaluated against consumers, not that any library change bumps them. The toHexString edit isn't a "new revert on existing function" — the revert already existed (require(value == 0, …)); only the revert encoding changed (Error(string)HexLengthInsufficient()). And that branch is unreachable for a correctly-sized length (after the loop value is always 0, and SquidFacet always passes a correct length), so there's zero reachable behavior change at the ABI level → PATCH-class, which doesn't cascade. So SquidFacet stays 1.0.0 and LibBytes' MINOR bump (the three new functions dominate) is correct.

The only real action item was your "surface it rather than fold it silently" point — done: the description now flags that SquidFacet's compiled bytecode/revert-encoding changes (dead-code only) so whoever next deploys it expects the 1.0.0 bytecode to differ.

Comment thread src/Facets/AcrossFacetV4.sol Outdated
function _convertAddressToBytes32(
address _address
) internal pure returns (bytes32) {
// TODO: migrate to LibBytes.toBytes32 — see LibBytes v1.1.0

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.

Bare // TODO breadcrumbs.

The 21 breadcrumbs reference "LibBytes v1.1.0" but not the tracking ticket (EXSC-618). Un-ticketed TODOs tend to become permanent — consider referencing the ticket ID so they're greppable and closable, e.g. // TODO(EXSC-618): migrate to LibBytes.toBytes32. Also worth confirming the repo's solhint config doesn't flag TODO, and noting this runs against the repo's "minimal comments" default (though the migration-breadcrumb rationale is defensible).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 0fbe4b5 — created a dedicated tracking ticket (EXSC-626) and rewrote all 21 breadcrumbs to // TODO(EXSC-626): migrate to LibBytes.… so they're greppable and closable.

Used a new ticket rather than EXSC-618 since EXSC-618 closes with this PR, whereas the migrations outlive it (each rides its facet's next version bump). .solhint.json has no max-line-length rule, so the longer comments are fine.

@lifi-qa-agent

lifi-qa-agent Bot commented Jul 15, 2026

Copy link
Copy Markdown

🔍 QA Review — EXSC-618

🔗 Linear Ticket · Pull Request #2055

🔁 Re-review — new commits pushed after the previous CHANGES_REQUESTED (2026-07-15T16:45:54Z). Analysing post-review changes only; all prior items re-evaluated against developer responses.

🧠 What this ticket does

LibBytes v1.1.0 consolidates the copy-pasted bytes32(uint256(uint160(addr))) / address(uint160(uint256(b))) pattern across ~21 facets into three helpers: toBytes32(address) (lossless widening), toAddress(bytes32) (checked narrowing — reverts NotAnAddress on high bits), and toAddressUnchecked(bytes32) (raw truncation for pre-guarded sites). A drive-by converts toHexString's require-with-string to a custom error HexLengthInsufficient. The ~21 existing call sites receive breadcrumb TODO comments pointing to the tracking ticket EXSC-626.

✅ Verdict: Pass — both previously flagged items are now resolved. Item 1 (High) is accepted with a well-reasoned technical justification and explicit PR description note; Item 2 (Low) is fixed in commit 0fbe4b5f9.


📊 Resolution Status — Re-review

# Item Severity Type Status Resolution
1 toHexString custom-error change silently alters SquidFacet bytecode 🟠 High Versioning / Audit ✅ Accepted with justification Developer documented decision in PR description; technical argument sound
2 Breadcrumb TODOs missing Linear ticket reference 🟢 Low Process ✅ Fixed Commit 0fbe4b5f9 — all 21 sites updated to // TODO(EXSC-626): format

🔎 Item-by-item resolution

1. [High] toHexString custom-error / SquidFacet bytecode — ✅ Accepted with justification

Developer chose not to bump SquidFacet to @custom:version 1.0.1. Response: the toHexString revert branch is unreachable for correctly-sized inputs — the loop consumes exactly 4 × length × 2 bits, so value is guaranteed to be 0 after processing an address-sized input with length=20. Only the revert encoding changes (Error(string)HexLengthInsufficient()), which is dead code at the SquidFacet call site. Per [CONV:VERSIONING], this is a PATCH-class change that does not cascade.

Technical verification: SquidFacet._callBridge calls toHexString(uint256(uint160(addr)), 20). An EVM address is 160 bits; the loop extracts 40 nibbles (160 bits total), leaving value == 0 by construction. The if (value != 0) guard is provably dead for this call site. The argument is sound.

The developer added an explicit note to the PR description: "Flagged so whoever next deploys SquidFacet knows its 1.0.0 bytecode will differ from the currently-deployed 1.0.0."

This satisfies the requirement to document the decision. No further action required.

SquidFacet @custom:version 1.0.0 confirmed on current branch head (unchanged). ✅

2. [Low] Breadcrumb TODOs — ✅ Fixed

Commit 0fbe4b5f92a5cee5b3825ed565ac57985ace284f (chore(breadcrumbs): reference EXSC-626 in LibBytes migration TODOs) rewrote all 21 breadcrumbs to // TODO(EXSC-626): migrate to LibBytes.<helper> — see LibBytes v1.1.0.

Spot-checked files — correct format confirmed:

  • src/Facets/AcrossFacetV4.sol:268// TODO(EXSC-626): migrate to LibBytes.toBytes32 — see LibBytes v1.1.0
  • src/Facets/AllBridgeFacet.sol:158,176,189// TODO(EXSC-626): migrate to LibBytes.toAddress (checked) / toBytes32
  • src/Facets/CelerCircleBridgeFacet.sol:128// TODO(EXSC-626): migrate to LibBytes.toBytes32
  • src/Periphery/ReceiverOIF.sol:93// TODO(EXSC-626): migrate to LibBytes.toAddress (checked)

Note: Using EXSC-626 (the tracking migration ticket) rather than EXSC-618 (which closes with this PR) is the correct choice — the migration work outlives this PR. git grep EXSC-626 will surface all remaining migration sites. ✅


✅ Ticket Coverage — High

PR delivers exactly what EXSC-618 describes: three helpers in LibBytes v1.1.0, a toHexString tidy-up, full unit tests, and breadcrumb TODOs now greppable via EXSC-626. All previously flagged items resolved.

🧪 Test Coverage

No new test files introduced in the post-review commit (comment-only change). Original assessment stands — test coverage is good; all three new helpers have round-trip, boundary, and revert-path tests with specific error selectors. No new gaps.

🔗 Downstream Impact

No change from original assessment. AuditRequired label correctly applied; waitForAudit flow owns enforcement. No redeploy required for the library itself. Lazy migration of the 21 call sites proceeds via EXSC-626.


QA Agent — 2026-07-16

@lifi-qa-agent lifi-qa-agent 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.

Requesting changes on 2 items — each requires either a code fix or an explicit acceptance comment with justification before this review is considered complete.

# Severity Type Issue / File
1 🟠 High Versioning / Audit toHexString custom-error change silently alters SquidFacet bytecode — @custom:version not bumped on src/Facets/SquidFacet.sol
2 🟢 Low Process Breadcrumb TODOs missing Linear ticket reference EXSC-618 across 21 call-site files

1. [High] toHexString custom-error change silently alters SquidFacet bytecode — no version bump

LibBytes.toHexString is internal pure and is inlined by the Solidity compiler into every consumer. SquidFacet._callBridge (line 187) is the sole caller. Replacing require(value == 0, "Strings: hex length insufficient") with if (value != 0) revert HexLengthInsufficient() changes the revert encoding emitted by SquidFacet (different selector, no revert string) without a @custom:version bump on SquidFacet.sol.

The versionControlAndAuditCheck CI only scans files changed in the PR diff (git diff --diff-filter=AM). Since SquidFacet.sol is not in the diff, CI will not flag this — the gap is invisible to automated enforcement. After this PR merges, the deployed SquidFacet bytecode will diverge from its audit20250508 audit artifact.

Required action: Either (a) bump SquidFacet to @custom:version 1.0.1, add a waitForAudit entry, and update auditLog.json — or (b) leave an explicit comment in this PR documenting the team's position that internal pure inlining does not count as a consumer version bump for the purposes of [CONV:VERSIONING], and capture that policy in the convention rule so it is consistently applied going forward. Option (b) requires a human reviewer to sign off on the policy decision before merge.

2. [Low] Breadcrumb TODOs missing Linear ticket reference EXSC-618

All 21 breadcrumb comments use the format // TODO: migrate to LibBytes.<helper> — see LibBytes v1.1.0 without the Linear ticket ID. Without a ticket reference, git grep EXSC-618 will not surface these breadcrumbs, and they risk becoming permanent once "LibBytes v1.1.0" is no longer a memorable shorthand.

Required action: Update breadcrumbs to include the ticket ID, e.g. // TODO: migrate to LibBytes.toBytes32 — see LibBytes v1.1.0 (EXSC-618). This is a comment-only change in 15 files.

💡 Once you've addressed the items above, re-apply the "Agent Review Request" label to trigger an automated re-review.

…XSC-618)

The 21 `// TODO: migrate to LibBytes` breadcrumbs now cite the dedicated
tracking ticket (EXSC-626) so they are greppable and closable, per review
feedback on #2055.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@0xDEnYO 0xDEnYO added the Agent Review Request triggers QA Agent Zeus label Jul 16, 2026
@github-actions github-actions Bot added QA AI Reviewing Zeus QA review in progress and removed Agent Review Request triggers QA Agent Zeus labels Jul 16, 2026

@lifi-qa-agent lifi-qa-agent 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.

✅ QA Pass — both CHANGES_REQUESTED items resolved: (1) SquidFacet bytecode change accepted with documented justification (dead-code path, PATCH-class per CONV:VERSIONING, note in PR description); (2) all 21 breadcrumbs updated to TODO(EXSC-626) format in commit 0fbe4b5. LibBytes v1.1.0 implementation, tests, and NatDoc are correct. AuditRequired label correctly applied — waitForAudit flow owns merge gate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AuditRequired QA AI Reviewing Zeus QA review in progress requires-types Trigger Types Bindings CI (ABI/type generation for lifi-contract-types)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants