# Incorrectly reading the offset from the received data parameter to get the depositNonce in the BranchBridgeAgent::anyFallback() function

anyFallback() reads depositNonce at wrong byte offset for flags 0x04-0x06, marking the wrong deposit as Failed and stranding the real one.

- Payout: Contest reward
- Severity: high
- Program: Maia DAO Ecosystem
- Credited: 0xStalin
- Disclosed: 2023-09-18
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2023-05-maia-findings/issues/169
- Library page: https://bugbountylibrary.com/r/c4%3A2023-05-maia%3AH-32
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** BranchBridgeAgent.sol anyFallback() cross-chain fallback handler
**Root cause:** anyFallback() reads depositNonce from data[21:26] (flags 0x04/0x05) and data[22:27] (flag 0x06), but the packed data places the 4-byte nonce at data[21:25] and data[22:26] respectively, so the read includes an extra byte and yields a wrong nonce.
**Steps:**
1. Enumerate each deposit flag's abi.encodePacked layout in BranchBridgeAgent (flags 0x00-0x06) to locate depositNonce byte range.
2. Note flags 0x04/0x05 pack `bytes1(flag), msg.sender(20), depositNonce(4)` => nonce at data[21:25]; flag 0x06 adds hTokens.length byte => nonce at data[22:26].
3. Inspect anyFallback(): flags 0x04/0x05 read `data[PARAMS_START_SIGNED:PARAMS_START_SIGNED+PARAMS_TKN_START]` = data[21:26]; flag 0x06 reads data[22:27].
4. Confirm off-by-one: read spans 5 bytes instead of 4, shifting the decoded uint32.
5. PoC: encode `abi.encodePacked(bytes1(0x01), msg.sender, nonce)` and show `uint32(bytes4(data[21:25]))` returns correct nonce while data[21:26] does not.
**How found:** Code-reading: systematically mapped the packed encoding of every deposit flag to find the nonce offset, then compared against the offsets anyFallback() actually reads, spotting the off-by-one for signed (0x04/0x05) and multi-asset signed (0x06) flags.
**Exploitation:** When a cross-chain call fails and anyFallback() runs, _clearDeposit() marks the misread nonce as Failed instead of the real one; the actual deposit stays pending/stuck and an unrelated depositNonce is wrongly flagged Failed.
**Insight:** When a contract packs variable-length structs with abi.encodePacked and later decodes by hardcoded offsets, enumerate every flag's layout and diff against the decode offsets; signed variants that prepend msg.sender shift the nonce by 20 bytes and are the classic place an off-by-one hides.
