# Incorrect source address decoding in RootBridgeAgent and BranchBridgeAgent's _requiresEndpoint breaks LayerZero communication

RootBridgeAgent and BranchBridgeAgent decode the wrong 20 bytes of LayerZero _srcAddress, so every cross-chain message reverts LayerZeroUnauthorizedCaller, breaking all bridging.

- Payout: Contest reward
- Severity: medium
- Program: Maia DAO
- Credited: 3docSec
- Disclosed: 2023-11-29
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-09-maia-findings/issues/348
- Library page: https://bugbountylibrary.com/r/c4%3A2023-09-maia%3AM-11
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:incomplete-fix technique:source-review vuln:access-control vuln:dos

**Target:** RootBridgeAgent/BranchBridgeAgent LayerZero lzReceive source-address validation
**Root cause:** LayerZero's lzReceive _srcAddress is abi.encodePacked(srcAddress, dstAddress), but the agents read the trailing 20 bytes (their own address) instead of the leading 20 bytes (the remote agent), so the caller check always fails.
**Steps:**
1. Deploy RootBridgeAgent and BranchBridgeAgent connected via LZEndpointMock on two chain IDs.
2. Call rba.syncBranchBridgeAgent(bba, branchChainId) and setDestLzEndpoint on both mocks.
3. Call rba.callOut{value:...}(...) to send root->branch; observe BranchBridgeAgent::lzReceiveNonBlocking reverts LayerZeroUnauthorizedCaller().
4. Call bba.callOut{value:...}(...) to send branch->root; observe RootBridgeAgent::lzReceiveNonBlocking reverts LayerZeroUnauthorizedCaller().
5. Confirm LzEndpointMock.send builds srcUaAddress = abi.encodePacked(msg.sender, dstAddr), inverting the assumed order.
**How found:** Code-reading of the _requiresEndpoint modifier against LayerZero's documented _srcAddress layout (src then dst packed), noticing the slice _srcAddress[20:] / [PARAMS_ADDRESS_SIZE:] extracts the destination (own) address rather than the source.
**Insight:** When validating LayerZero _srcAddress, remember it is abi.encodePacked(srcAddress, dstAddress); the source is the FIRST 20 bytes, not the last. Cross-check against the endpoint mock's send() which packs msg.sender first.
