Skip to content

LZ Bridge (L-02): Emit actual LZ native fee in Bridged event - #270

Merged
zeroxnoodle merged 1 commit into
lz-bridge-upgrade-fixfrom
fix/lz-bridge-upgrade-event-fee
May 6, 2026
Merged

zeroxnoodle merged 1 commit into
lz-bridge-upgrade-fixfrom
fix/lz-bridge-upgrade-event-fee

Conversation

@zeroxnoodle

@zeroxnoodle zeroxnoodle commented May 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Bug Fixes

    • Cross-chain bridge now accurately tracks and reports actual fees charged separately from supplied ETH, improving fee transparency.
    • Enhanced handling of excess ETH when bridging with overpayment.
  • Tests

    • Added and updated tests to verify correct fee reporting and overpayment handling.

burnAndSend now returns the MessagingReceipt from the LZ endpoint, and
LZCrossChainBridge.sendOhm surfaces receipt.fee.nativeFee in the Bridged
event alongside the supplied msg.value. Previously the event recorded
msg.value, which overstates the paid fee when callers overpay and the
LZ endpoint refunds the excess to msg.sender.

Extend tests: BurnAndSend verifies receipt.fee.nativeFee matches the
estimate and is strictly less than totalSent on overpayment; SendOhm
adds test_sendOhm_emitsActualNativeFeeOnOverpayment to assert the

Bridged event reports the real nativeFee and the supplied msgValue.
@coderabbitai

coderabbitai Bot commented May 4, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

The PR updates the LayerZero cross-chain bridge to capture and propagate the MessagingReceipt returned by the LayerZero endpoint's send method, exposing the actual charged native fee instead of the caller's supplied msg.value through the Bridged event and the ILZBridgeGateway.burnAndSend interface.

Changes

LayerZero Receipt Propagation

Layer / File(s) Summary
Interface Contracts
src/policies/interfaces/ILZBridgeGateway.sol, src/periphery/interfaces/ILZCrossChainBridge.sol
ILZBridgeGateway.burnAndSend now declares a named return value MessagingReceipt memory receipt. ILZCrossChainBridge.Bridged event signature changes from Bridged(address, uint256, uint32, uint256 fees) to Bridged(address, uint256, uint32, uint256 nativeFee, uint256 msgValue) to distinguish actual LayerZero-charged fee from caller-supplied ETH.
Implementation
src/policies/bridge/LZBridgeGateway.sol, src/periphery/bridge/LZCrossChainBridge.sol
LZBridgeGateway.burnAndSend assigns the LayerZero send result to its named return variable instead of a local declaration. LZCrossChainBridge.sendOhm captures the MessagingReceipt from gateway.burnAndSend and emits Bridged with receipt.fee.nativeFee (actual fee) and msg.value (supplied value). Imports updated to include MessagingReceipt.
Tests
src/test/policies/bridge/LZBridgeGateway/LZBridgeGateway_BurnAndSend.t.sol, src/test/periphery/bridge/LZCrossChainBridge/LZCrossChainBridge_SendOhm.t.sol
Existing tests capture MessagingReceipt return value and assert fee fields. New test test_sendOhm_emitsActualNativeFeeOnOverpayment verifies that overpayment is not charged—user is debited only the actual native fee while both bridge and gateway retain zero ETH.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • OlympusDAO/olympus-v3#220: Introduced the LayerZero bridge gateway; this PR exposes its MessagingReceipt return value through the cross-chain bridge API.

Suggested reviewers

  • 0xJem

Poem

🐰 A bridge now speaks what LayerZero knows,
The receipt returns, true fees it shows,
No more guessing—what's charged comes clear,
Overpayment refunds, user held dear! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: emitting the actual LZ native fee in the Bridged event instead of just msg.value, which is the core objective addressed across all modified files.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/lz-bridge-upgrade-event-fee

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 and usage tips.

@0xJem

0xJem commented May 5, 2026

Copy link
Copy Markdown
Member

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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.

🧹 Nitpick comments (1)
src/periphery/interfaces/ILZCrossChainBridge.sol (1)

29-35: Bridged event ABI change breaks existing off-chain consumers.

The event topic (keccak256("Bridged(address,uint256,uint32,uint256)")) changes to keccak256("Bridged(address,uint256,uint32,uint256,uint256)"). Any subgraph, analytics pipeline, or frontend listener that subscribed to the old signature will silently stop receiving events after the contract is upgraded.

Ensure that all subgraphs, indexers, and event decoders (frontend, off-chain services) are updated to the new ABI before or immediately after deployment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/periphery/interfaces/ILZCrossChainBridge.sol` around lines 29 - 35, The
Bridged event signature was changed (event Bridged(...) now includes msgValue)
which will break any off-chain consumers relying on the old keccak256 topic; to
fix, either restore the original Bridged ABI or preserve backward compatibility
by keeping the original Bridged declaration and adding a new event (e.g.,
BridgedV2 or BridgedWithMsgValue) or emitting both events from the bridging
logic; then update all off-chain artifacts (ABI files, subgraph
mappings/manifest, indexer decoders, frontend listeners) to the new event
name/signature and re-deploy/re-index so consumers pick up the change — check
and modify usages referring to the Bridged symbol and any Graph mappings that
decode its topics.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/periphery/interfaces/ILZCrossChainBridge.sol`:
- Around line 29-35: The Bridged event signature was changed (event Bridged(...)
now includes msgValue) which will break any off-chain consumers relying on the
old keccak256 topic; to fix, either restore the original Bridged ABI or preserve
backward compatibility by keeping the original Bridged declaration and adding a
new event (e.g., BridgedV2 or BridgedWithMsgValue) or emitting both events from
the bridging logic; then update all off-chain artifacts (ABI files, subgraph
mappings/manifest, indexer decoders, frontend listeners) to the new event
name/signature and re-deploy/re-index so consumers pick up the change — check
and modify usages referring to the Bridged symbol and any Graph mappings that
decode its topics.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 611802e7-b96b-41c3-b266-30329f46c228

📥 Commits

Reviewing files that changed from the base of the PR and between 8bc9492 and eac1e78.

📒 Files selected for processing (6)
  • src/periphery/bridge/LZCrossChainBridge.sol
  • src/periphery/interfaces/ILZCrossChainBridge.sol
  • src/policies/bridge/LZBridgeGateway.sol
  • src/policies/interfaces/ILZBridgeGateway.sol
  • src/test/periphery/bridge/LZCrossChainBridge/LZCrossChainBridge_SendOhm.t.sol
  • src/test/policies/bridge/LZBridgeGateway/LZBridgeGateway_BurnAndSend.t.sol

@zeroxnoodle
zeroxnoodle merged commit 487dd38 into lz-bridge-upgrade-fix May 6, 2026
12 checks passed
@zeroxnoodle
zeroxnoodle deleted the fix/lz-bridge-upgrade-event-fee branch May 6, 2026 14:39
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.

3 participants