xanoutas/solidity-security-findings huggingface dataset:
RootBridgeAgent:_createSettlementMultiple should encode 0x82 instead of 0x02 when _hasFallbackToggled is set
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/main/src/RootBridgeAgent.sol#L1090


# Vulnerability details

## Impact
Users won't get a fallback call even if the _hasFallbackToggled is set in router/callOutAndBridgeMultiple, which can lead to the wrong assumption that the call has successfully executed on the destination chain without retry/redeem, resulting in assets lost.

## Proof of Concept
In RootBridgeAgent:_createSettlementMultiple, we encode the DEPOSIT FLAG as:

```solidity
        // Prepare data for call with settlement of multiple assets
        _payload = abi.encodePacked(
            _hasFallbackToggled ? bytes1(0x02) & 0x0F : bytes1(0x02),
            _recipient,
```

Since bytes1(0x02) & 0x0F == 0x02, no matter if _hasFallbackToggled is true or false, the deposit flag is always 0x02, making the fallback won't happen.

So, if a user is using a router that calls callOutAndBridgeMultiple with _hasFallbackToggled set, the fallback won't happen when the call fails on the dst chain.

## Tools Used
Manual Review.

## Recommended Mitigation Steps
Change to:
```solidity
        // Prepare data for call with settlement of multiple assets
        _payload = abi.encodePacked(
            _hasFallbackToggled ? bytes1(0x82) : bytes1(0x02),
```


## Assessed type

Context
Inappropriate Extraction of Remote Address from LayerZero's `_srcAddress` Bytes Object
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgent.sol#L1212
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BranchBridgeAgent.sol#L943


# Vulnerability details

## Overview

We discovered a remote address verification issue due to inappropriate extraction of the remote address from the source chain. This issue affects the Maia protocol's interaction with LayerZero and could lead to cross-chain message delivery failures.

### Background

#### LayerZero Trusted Remotes

LayerZero defines a Trusted Remote as a 40-byte object used to identify another contract from which a user application contract (UA) will receive messages. The Trusted Remote consists of the concatenated bytes of the REMOTE contract address and the LOCAL contract address. This is the same as the one named `bytes calldata _srcAddress` in `lzReceive` parameters.

#### Address Slicing

Array slices in Solidity are views on a contiguous portion of an array, represented as `x[start:end]`. Both `start` and `end` are optional, defaulting to 0 and the length of the array, respectively. Array slices do not have a type name and are implicitly convertible to arrays of their underlying type. Index access within a slice is relative to the start of the slice.

## Issue Description

The source address verification issue arises when extracting the source contract's address from the `_srcAddress` parameter within the Maia protocol's LayerZero-related contracts. This parameter represents the Trusted Remote of the source User Application contract (UA), but the verification process assumes the last 20 bytes of `_srcAddress` correspond to the source contract's address. However, this assumption is not accurate, potentially leading to incorrect address extraction. according to LayerZero contracts, this is how the node is [encoding the `_srcAddress`](https://github.com/LayerZero-Labs/LayerZero/blob/48c21c3921931798184367fc02d3a8132b041942/contracts/UltraLightNodeV2.sol#L111C9-L111C90) before calling the `lzReceive` on the destination contract:

```solidity
bytes memory pathData = abi.encodePacked(_packet.srcAddress, _packet.dstAddress);
```

#### Verification in Root Bridge Agent

In the Root Bridge Agent contract `lzReceive`, which is responsible for receiving messages on the destination chain. The `_srcAddress` parameter is passed to this function and is expected to represent the source UA's Trusted Remote. However, the verification process in `requiresEndpoint` takes the last 20 bytes `_srcAddress[20:]` as the source contract's address, assuming that the first 20 bytes are the local contract's address. ([`PARAMS_ADDRESS_SIZE` is equal to 20](https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/interfaces/BridgeAgentConstants.sol#L37))

```solidity
if (_srcAddress.length != 40) revert LayerZeroUnauthorizedCaller();

if (getBranchBridgeAgent[_src
Fee-on-transfer tokens are not accounted for correctly
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/ArbitrumBranchPort.sol#L50-L70
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BranchPort.sol#L502-L504


# Vulnerability details

## Impact
The protocol is intended to hold any token (as these can be permissionlessly added). These tokens are deposited to the `ArbitrumBranchPort` through a call to the `ArbitrumBranchBridgeAgent::depositToPort()`. However, the `ERC20` token interactions and internal accounting do not take into account the actual amounts transferred, but naively accept the deposit `amount` as specified by the depositor and mints corresponding `hTokens` to the user.

As Ulysses is intended to work with all tokens some users will quickly realize that it makes more sense to deposit their fee-on-transfer tokens, like `PAXG`, into the port because this effectively "wraps" them for intra- or inter-chain use without extra transfer fees. The token economic incentive is thus to deposit fee-on-transfer tokens into the ports. Should these types of tokens be used in the protocol the internal accounting would become out of sync with the actual token balances and will cause losses for some users.  

Due to this leak in value and violation of a main invariant (for some specific subset of tokens), this has been evaluated to medium. 

It should be noted that tokens are added permissionlessly, and the docs don't expressly advise users not to use fee-on-transfer tokens, thus the assumption would be that they are expected to be compatible.

## Proof of Concept
For example, consider a token with a 5% fee on transfer. The user, Bob, deposits via `ArbitrumBranchPort::depositToPort()`. Note that 5% is a large fee for demonstration purposes.

We can see in the below code snippet that the `_deposit` amount is minted without checking the actual transferred value: 
```
    function depositToPort(address _depositor, address _recipient, address _underlyingAddress, uint256 _deposit)
        external
        override
        lock
        requiresBridgeAgent
    {
        // Save root port address to memory
        address _rootPortAddress = rootPortAddress;


        // Get global token address from root port
        address _globalToken = IRootPort(_rootPortAddress).getLocalTokenFromUnderlying(_underlyingAddress, localChainId);


        // Check if the global token exists
        if (_globalToken == address(0)) revert UnknownGlobalToken();


        // Deposit Assets to Port
        _underlyingAddress.safeTransferFrom(_depositor, address(this), _deposit);


        // Request Minting of Global Token
        IRootPort(_rootPortAddress).mintToLocalBranch(_recipient, _globalToken, _deposit);
    }
```

Bob specified `100` tokens. When he transferred to the port, 5 tokens were deducted as fees with the balance going to the intended destination. The `ArbitrumBranchPort` receives 95 tokens, but mints `100` `hTokens
Airdropped native gas tokens can be left on the RootBridgeAgent in case of an execution failure, leaving them open for grab
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgent.sol#L749


# Vulnerability details

## Impact
Failing message executions can lead to value being left on the RootBridgeAgent and drained by someone else later.

## Proof of Concept
If a message fails either here https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgent.sol#L754 or here https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgent.sol#L779 (if a fallback is not enabled), any airdropped native gas tokens will be left on the `RootBridgeAgent`.

That value can be drained using a failing call that has a fallback.

### Test
```solidity
    function testValueLockedInRootBridgeAgent() public {
        assertEq(address(coreRootBridgeAgent).balance, 0);

        switchToLzChain(avaxChainId);
        
        vm.deal(owner, 100 ether);

        avaxCoreBridgeAgent.callOut{value: owner.balance}(
            payable(owner),
            abi.encodePacked(uint8(0)),
            GasParams(300_000, 0.1 ether)
        );

        switchToLzChain(rootChainId);

        assertEq(address(coreRootBridgeAgent).balance, 0, "Root bridge agent balance changed");
        assertEq(vm.addr(1000).balance, 0);

        switchToLzChain(ftmChainId);

        vm.deal(vm.addr(1000), 100 ether);

        DepositInput memory depositInput = DepositInput({
            hToken: address(0),
            token: address(0),
            amount: 0,
            deposit: 0
        });

        vm.prank(vm.addr(1000));
        ftmCoreBridgeAgent.callOutSignedAndBridge{value: vm.addr(1000).balance}(
            payable(vm.addr(1000)),
            "",
            depositInput,
            GasParams(1_500_000, 0),
            true
        );
        
        switchToLzChain(rootChainId);

        assertEq(vm.addr(1000).balance, 0, "Refundee balance changed");
        assertNotEq(address(coreRootBridgeAgent).balance, 0, "Root bridge agent was drained");
    }
```

### Results
```
  Error: Root bridge agent balance changed
  Error: a == b not satisfied [uint]
        Left: 1000000000000000000000
       Right: 0
  Error: Refundee balance changed
  Error: a == b not satisfied [uint]
        Left: 999999282191708531036
       Right: 0
  Error: Root bridge agent was drained
  Error: a != b not satisfied [uint]
        Left: 0
       Right: 0
```
## Tools Used
Manual Review

## Recommended Mitigation Steps
The airdropped native gas tokens need to be sent back to a refundee in case of a message execution failure.



## Assessed type

Other
replenishReserves() will not work with tokens with transfer fees
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/main/src/BranchPort.sol#L181


# Vulnerability details

## Proof of Concept
BranchPort.replenishReserves() (https://github.com/code-423n4/2023-09-maia/blob/main/src/BranchPort.sol#L167-L185) - allow approved address to repay borrowed reserves.

When the function is executed, the line is called:
require(ERC20(_token).balanceOf(address(this)) - currBalance == _amount, "Port Strategy Withdraw Failed");

However, there are ERC20 tokens with transfer fees:
https://github.com/d-xo/weird-erc20#fee-on-transfer

With tokens like this, require(ERC20(_token).balanceOf(address(this)) - currBalance == _amount, "Port Strategy Withdraw Failed") will always fail.

## Tools Used
Manual review

## Recommended Mitigation Steps
Take into account the use of similar tokens in the function’s operation


## Assessed type

ERC20
Incorrect Fallback Flag in _createSettlementMultiple Function
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/ffbe532c6f5224a55ce099b4016bd8806bdbc913/src/RootBridgeAgent.sol#L1090
https://github.com/code-423n4/2023-09-maia/blob/ffbe532c6f5224a55ce099b4016bd8806bdbc913/src/BranchBridgeAgent.sol#L638-L660
https://github.com/code-423n4/2023-09-maia/blob/ffbe532c6f5224a55ce099b4016bd8806bdbc913/src/BranchBridgeAgent.sol#L651


# Vulnerability details

## Impact

The incorrect flag setting prevents the fallback mechanism from executing in the destination chain. As a result, users cannot retrieve their tokens, affecting the reliability of the protocol.

## Proof of Concept

When settling multiple assets and perform a remote call to a branch chain, the function `RootBridgeAgent._createSettlementMultiple` sets the fallback flag based on the `_hasFallbackToggled` variable. The flag is set to either `bytes1(0x02) & 0x0F` or `bytes1(0x02)`.

        _hasFallbackToggled ? bytes1(0x02) & 0x0F : bytes1(0x02)

https://github.com/code-423n4/2023-09-maia/blob/ffbe532c6f5224a55ce099b4016bd8806bdbc913/src/RootBridgeAgent.sol#L1090

The logic error is both flag settings evaluate to `0x02`:

0x02 & 0x0F = 0x02 (0000 0010 & 0000 1111 = 0000 0010)

This means that the fallback flag is always set to `0x02`, regardless of whether `_hasFallbackToggled` is true or false.

The BranchBridgeAgent contract checks for a flag value of `_payload[0] == 0x82` to trigger the fallback mechanism. Because the flag is incorrectly set to `0x02`, the fallback mechanism is never triggered, even when it should be.

            // DEPOSIT FLAG: 2 (Multiple Settlement)
        } else if (flag == 0x02) {
            // Parse recipient
            address payable recipient = payable(address(uint160(bytes20(_payload[PARAMS_START:PARAMS_START_SIGNED]))));

            // Parse deposit nonce
            nonce = uint32(bytes4(_payload[22:26]));

            //Check if tx has already been executed
            if (executionState[nonce] != STATUS_READY) revert AlreadyExecutedTransaction();

            //Try to execute remote request
            // Flag 2 - BranchBridgeAgentExecutor(bridgeAgentExecutorAddress).executeWithSettlementMultiple(recipient, localRouterAddress, _payload)
            _execute(
                _payload[0] == 0x82,
                nonce,
                recipient,
                abi.encodeWithSelector(
                    BranchBridgeAgentExecutor.executeWithSettlementMultiple.selector,
                    recipient,
                    localRouterAddress,
                    _payload
                )
            );

https://github.com/code-423n4/2023-09-maia/blob/ffbe532c6f5224a55ce099b4016bd8806bdbc913/src/BranchBridgeAgent.sol#L638-L660

https://github.com/code-423n4/2023-09-maia/blob/ffbe532c6f5224a55ce099b4016bd8806bdbc913/src/BranchBridgeAgent.sol#L651

## Tools Used

Manual

## Recommended Mitigation Steps

Correct the fallback flag setting in the `_createSettlementMultiple` function.

```diff
-        _hasFallba
Incomplete Implementation of Non-Blocking Pattern in Maia Protocol's LayerZero
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/ffbe532c6f5224a55ce099b4016bd8806bdbc913/src/BranchBridgeAgent.sol#L578-L584
https://github.com/code-423n4/2023-09-maia/blob/ffbe532c6f5224a55ce099b4016bd8806bdbc913/src/BranchBridgeAgent.sol#L720
https://github.com/code-423n4/2023-09-maia/blob/ffbe532c6f5224a55ce099b4016bd8806bdbc913/src/BranchBridgeAgent.sol#L748-L751


# Vulnerability details

## Impact

If a transaction fails on the destination chain, the channel between the source and destination chains becomes blocked. This effectively halts the operation of the protocol until the issue is resolved or a force resume message is delivered by the dstUA contract.

## Proof of Concept

If a transaction fails in the destination chain, the channel between the source chain and destination chain will be blocked. The source chain cannot send any message to the destination chain until the transaction is success or force resume message delivery, only by the dstUA contract.

https://layerzero.gitbook.io/docs/faq/messaging-properties#message-ordering

        LayerZero provides ordered delivery of messages from a given sender to a destination chain, i.e. srcUA-> dstChain. In other words, the message order nonce is shared by all dstUA on the same dstChain. That's why a STORED message blocks the message pathway from srcUA to all dstUA on the same destination chain.  If it isn't necessary to preserve the sequential nonce property for a particular dstUA the sender must add the nonce into the payload and handle it end-to-end within the UA.  UAs can implement a non-blocking pattern in their contract code. 

LayerZero recommends implementing a non-blocking pattern where all errors and exceptions are caught locally for future retries. This ensures that the channel remains open even if a transaction fails.

        (bool success, bytes memory reason) = address(this).excessivelySafeCall(
            gasleft(),
            150,
            abi.encodeWithSelector(this.nonblockingLzReceive.selector, _srcChainId, _srcAddress, _nonce, _payload)
        );
        // try-catch all errors/exceptions
        if (!success) {
            _storeFailedMessage(_srcChainId, _srcAddress, _nonce, _payload, reason);
        }

https://github.com/LayerZero-Labs/solidity-examples/blob/2116a67dc72e4d13d66e6cc9107096ae705a07b7/contracts/lzApp/NonblockingLzApp.sol#L35-L38

However, in Maia protocol, we can see that not all error, revert is caught

        function lzReceive(uint16, bytes calldata _srcAddress, uint64, bytes calldata _payload) public override {
            address(this).excessivelySafeCall(
                gasleft(),
                150,
                abi.encodeWithSelector(this.lzReceiveNonBlocking.selector, msg.sender, _srcAddress, _payload)
            );
        }

https://github.com/code-423n4/2023-09-maia/blob/ffbe532c6f5224a55ce099b4016bd8806bdbc913/src/BranchBridgeAgent.sol#L578-L584

In contrast, Maia's current implementation does not catch all e
Insufficient Gas Griefing
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/main/src/BranchBridgeAgent.sol#L717
https://github.com/code-423n4/2023-09-maia/blob/main/src/BranchBridgeAgent.sol#L737
https://github.com/code-423n4/2023-09-maia/blob/main/src/RootBridgeAgent.sol#L754
https://github.com/code-423n4/2023-09-maia/blob/main/src/RootBridgeAgent.sol#L779
https://github.com/code-423n4/2023-09-maia/blob/main/src/VirtualAccount.sol#L74
https://github.com/code-423n4/2023-09-maia/blob/main/src/VirtualAccount.sol#L101


# Vulnerability details

## Impact
Insufficient gas griefing attacks can be performed on contracts which accept data and use it in a sub-call on another contract. If the sub-call fails, either the whole transaction is reverted, or execution is continued. In the case of a relayer contract, the user who executes the transaction, the 'forwarder', can effectively censor transactions by using just enough gas to execute the transaction, but not enough for the sub-call to succeed.

## Proof of Concept

```solidity
File: src/BranchBridgeAgent.sol

717:         (bool success,) = bridgeAgentExecutorAddress.call{value: address(this).balance}(_calldata);

737:         (bool success,) = bridgeAgentExecutorAddress.call{value: address(this).balance}(_calldata);

```

```solidity
File: src/RootBridgeAgent.sol

754:         (bool success,) = bridgeAgentExecutorAddress.call{value: address(this).balance}(_calldata);

779:         (bool success,) = bridgeAgentExecutorAddress.call{value: address(this).balance}(_calldata);

```

```solidity
File: src/VirtualAccount.sol

74:             if (isContract(_call.target)) (success, returnData[i]) = _call.target.call(_call.callData);

101:             if (isContract(_call.target)) (success, returnData[i]) = _call.target.call{value: val}(_call.callData);

```

## Tools Used
VS Code

## Recommended Mitigation Steps
There are two options to prevent insufficient gas griefing:
- Only allow trusted users to relay transactions.
- Require that the forwarder provides enough gas.


## Assessed type

Other
When using BaseBranchRouter as a router on the 'Arbitrum' branch, we are unable to invoke the 'callOutAndBridge' function.
Audit: 2023-09-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BaseBranchRouter.sol#L95-L100
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BaseBranchRouter.sol#L167-L168
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BranchBridgeAgent.sol#L224
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BranchBridgeAgent.sol#L824
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/ArbitrumBranchPort.sol#L134


# Vulnerability details

## Impact
All branches have one core router and several subsidiary branches. The core branches handle system calls, and users can create deposits using subsidiary branches. The function utilized for creating deposits is the `callOutAndBridge` function. However, in the `Arbitrum` branch, this function is not available for use. This restriction has system-wide implications because the Arbitrum branch should function similarly to other local branches.

## Proof of Concept
When a user attempts to make a deposit  in other branches, the `local hTokens` are transferred from the user to the `local branch port` and subsequently burned.
However, in `Arbitrum`, there is no differentiation between `local` and `global hTokens`. Consequently, they are sent directly to the `root port`.
When a user invokes the `callOutAndBridge` function in a branch, it involves the transfer of `hTokens` and `underlying tokens` from the `user` to the `router`. Additionally, the `router` approves the `local port` to access and utilize these tokens.
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BaseBranchRouter.sol#L95
```
_transferAndApproveToken(_dParams.hToken, _dParams.token, _dParams.amount, _dParams.deposit);
```
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BaseBranchRouter.sol#L167-L168
```
_hToken.safeTransferFrom(msg.sender, address(this), _amount - _deposit);
ERC20(_hToken).approve(_localPortAddress, _amount - _deposit);

_token.safeTransferFrom(msg.sender, address(this), _deposit);
ERC20(_token).approve(_localPortAddress, _deposit);
```
Then we call the 'callOutAndBridge' function within the `bridge agent`. Within this function, a deposit is created.
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BranchBridgeAgent.sol#L224
```
_createDeposit(_depositNonce, _refundee, _dParams.hToken, _dParams.token, _dParams.amount, _dParams.deposit);
```
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BranchBridgeAgent.sol#L824
```
IPort(localPortAddress).bridgeOut(msg.sender, _hToken, _token, _amount, _deposit);
```
We've now reached the `_bridgeOut` function in `ArbitrumBranchPort`. 
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff594996