xanoutas/solidity-security-findings huggingface dataset:
Multisig wallets that call bridgeAndExecute() could cause tokens to be stolen
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/main/src/UTB.sol#L259-L274


# Vulnerability details

## Impact
UTB.bridgeAndExecute() is used to execute a cross-chain transaction for the users from the host chain. 

The issue is that the refund address that is passed as part of the bridgeInstructions struct is the msg.sender on the host chain as confirmed by the developers and in the [natspec](https://github.com/code-423n4/2024-01-decent/blob/main/src/UTBExecutor.sol#L17) in the transaction executing contract [UTBExecutor.sol](https://github.com/code-423n4/2024-01-decent/blob/main/src/UTBExecutor.sol#L41-L82). 

This will be a problem if the msg.sender is a multisig wallet contract as the wallet holders might not have control over the same address in the destination chain, causing the received tokens to be lost. 

In the worst case, the tokens could be sent to an undeployed address and an attacker could seize the opportunity to possibly steal the tokens by taking control of the refund address on the destination chain. (see [Wintermute hack article](https://rekt.news/wintermute-rekt/))

## Proof of Concept

https://github.com/code-423n4/2024-01-decent/blob/main/src/UTBExecutor.sol#L41-L82
```
function execute(
        address target,
        address paymentOperator,
        bytes memory payload,
        address token,
        uint amount,
        address payable refund,
        uint extraNative
    ) public onlyOwner {
        bool success;
        if (token == address(0)) {
            (success, ) = target.call{value: amount}(payload);
            if (!success) {
                (refund.call{value: amount}(""));
            }
            return;
        }

        uint initBalance = IERC20(token).balanceOf(address(this));

        IERC20(token).transferFrom(msg.sender, address(this), amount);
        IERC20(token).approve(paymentOperator, amount);

        if (extraNative > 0) {
            (success, ) = target.call{value: extraNative}(payload);
            if (!success) {
                (refund.call{value: extraNative}(""));
            }
        } else {
            (success, ) = target.call(payload);
        }

        uint remainingBalance = IERC20(token).balanceOf(address(this)) -
            initBalance;

        if (remainingBalance == 0) {
            return;
        }

        IERC20(token).transfer(refund, remainingBalance);
    }
```

## Tools Used
Manual Analysis

## Recommended Mitigation Steps
Provide the user with the ability to provide a custom refund address on the destination chain, so that multisig wallet owners can provide their address on the destination chain. 


## Assessed type

Other
sgReceive can be called from any contract on the sending chain
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/bridge_adapters/StargateBridgeAdapter.sol#L183-L190


# Vulnerability details

## Impact

Due to not validating the `srcAddress` in `sgReceive` it is possible to send messages to `sgReceive` from any contract on the sending chain.
None of the parameters inside the `sgReceive` are validated on the receiving side but rather they are checked on the sending side. 

The impact is allowing attackers to invoke [high approvals](https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/bridge_adapters/StargateBridgeAdapter.sol#L207).
If there are hanging tokens inside the `StargateBridgeAdapter.sol` contract they can be stolen, so I'm marking this as a high severity issue. 
Also, if the logic upstream in the `UTB:receiveFromBridge` changes there could be serious issues down the line.  

## Proof of Concept

Transferring funds and sending payloads through stargate works as follows: 

1. The `StargateBridgeAdapter:bridge()`function invokes the StargateComposer:swap() function.
2. This function encodes the `to` address which is the `StargateBridgeAdapter` contract address on the receiving chain, the `msg.sender` and the original payload: https://stargateprotocol.gitbook.io/stargate/stargate-composability/stargatecomposer.sol#buildpayload
3. When it is being received it will slice this payload and the toAddress is the first 20 bytes, the second 20 bytes is the `msg.sender` and the rest is the original payload: https://stargateprotocol.gitbook.io/stargate/stargate-composability/stargatecomposer.sol#sgreceive
4. So when `sgReceive` gets invoked the `msg.sender` on sending chain is the `srcAddress` parameter on the receiving chain.
5. If this parameter is not validated it is possible to send messages from any contract on the sending chain to `sgReceive` on the receiving chain with any kind of arbitrary data. 

## Tools Used
- Manual review

## Recommended Mitigation Steps

Validate the [`srcAddress`](https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/bridge_adapters/StargateBridgeAdapter.sol#L185) parameter in `sgReceive` and make sure it is the `StargateBridgeAdapter` contract address on the sending chain.


## Assessed type

Access Control
Transfer does not work on zkSync
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTBFeeCollector.sol#L71


# Vulnerability details

Fees are stuck inside the UTBFeeCollector contract on zkSync

## Impact
The project README file states that the scope of blockchains where the project is deployed are those supported by LayerZero (https://layerzero.gitbook.io/docs/technical-reference/mainnet/supported-chain-ids),
and this includes zkSync era. The transfer function does not work on zkSync since it's transferring only 2300 gas and this will always revert, so the [`redeemFees`](https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTBFeeCollector.sol#L71) function couldn't be invoked. 

The impact is all the fees generated by the protocol on zkSync cannot be redeemed and are permanently locked inside the contract.

## Proof of Concept
See the following resources describing the issue:
- https://twitter.com/zksync/status/1644139364270878720
- https://consensys.io/diligence/blog/2019/09/stop-using-soliditys-transfer-now/

## Tools Used
- Manual review

## Recommended Mitigation Steps
Replace the transfer function with the following:

```solidity
    (bool success, ) = _to.call{value: _amount}("");
    require(success, "Transfer failed.");
}
```


## Assessed type

ETH-Transfer
modifier userIsWithdrawing breaks the CEI security guidelines when applied on a function, making it prone to re-entrancy attack. e.g. removeLiquidityEth & removeLiquidityWeth
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L63-L64
https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L313-L315
https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L330-L332


# Vulnerability details

When the modifier `userIsWithdrawing` is applied on any function, it will break the commonly used CEI (Check-Effects-Interact) pattern for security guidance. Because, the setting of balanceOf would always be the last statement. (after any external contract calls inside the function)

## Impact
The impact is on public facing functions of, `removeLiquidityEth` & `removeLiquidityWeth`

On applying the modifier, the functions losses the CEI pattern (since setting of balanceOf becomes the last statement now) and are prone to Reentrancy attack.

## Tools Used
Manual review

## Recommended Mitigation Steps
Remove the following line of code in modifier `userIsWithdrawing`.

```solidity
60:    modifier userIsWithdrawing(uint256 amount) {
61:        uint256 balance = balanceOf[msg.sender];
62:        require(balance >= amount, "not enough balance");
63:        _;
  :-       balanceOf[msg.sender] -= amount;
65:    }
```

Append the same line to applicable functions, before any external calls.

```solidity
313:    function removeLiquidityEth(
314:        uint256 amount
315:    ) public onlyEthChain userIsWithdrawing(amount) {
   :+       balanceOf[msg.sender] -= amount;
316:        dcntEth.burn(address(this), amount);
317:        weth.withdraw(amount);
318:        payable(msg.sender).transfer(amount);
319:    }
```

```solidity
330:    function removeLiquidityWeth(
331:        uint256 amount
332:    ) public userIsWithdrawing(amount) {
   :+       balanceOf[msg.sender] -= amount;
333:        dcntEth.burn(address(this), amount);
334:        weth.transfer(msg.sender, amount);
335:    }
```


## Assessed type

Reentrancy
WETH is transferred to incorrect address in function _executeWeth()
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentBridgeExecutor.sol#L24


# Vulnerability details

## Impact
In the function _executeWeth(), if the call to the `target` (i.e. destination bridge adapter) fails (due payload call failure or some other reason) or the target partially spends the WETH tokens, the remaining WETH is transferred to the `from` address. This `from` address is the source chain's decentBridgeAdapter contract. The issue is that the WETH will be transferred to the equivalent of the `from` address (src chain adapter) but on the destination chain, which may or may not be owned by anyone. This would mean loss of WETH for the user who was expecting the tokens. 

## Proof of Concept

1. If the user calls the bridgeAndExecute() function on the source chain, the `from` address is set to be the msg.sender over [here](https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L105). This msg.sender is the decentBridgeAdapter contract on the source chain since it is the one that calls the bridgeWithPayload() function on the source router (see [here](https://github.com/code-423n4/2024-01-decent/blob/07ef78215e3d246d47a410651906287c6acec3ef/src/bridge_adapters/DecentBridgeAdapter.sol#L117)). The execution path would like as follows:

bridgeAndExecute (UTB) => callBridge (UTB) => bridge (Adapter) => bridgeWithPayload (Router)

2. This `from` address is then decoded on the destination chain in the function onOFTReceived().

```solidity
File: DecentEthRouter.sol
238:     function onOFTReceived(
239:         uint16 _srcChainId,
240:         bytes calldata,
241:         uint64,
242:         bytes32,
243:         uint _amount,
244:         bytes memory _payload
245:     ) external override onlyLzApp {
246:         (uint8 msgType, address _from, address _to, bool deliverEth) = abi
247:             .decode(_payload, (uint8, address, address, bool));
```

3. The onOFTReceived() function calls the execute() function on the DecentBridgeExecutor, which calls the _executeWeth() function. The following occurs in it:
 - On Line 31-33, the target contract (destination adapter) is called with the callPayload (i.e. receiveFromBridge function). If this call fails due to payload call failure after post bridge swap or some other reason, the call would revert causing the value of success of be false.
 - On Line 35-36, since the call to target failed, the whole WETH amount is transferred to the `from` address, which is the source chain's adapter address being used by someone or no one on the destination chain. Due to this, the tokens the user expects will be lost forever.
 - On Line 37, we return early.
 - Note - the same issue exists on Line 44 where if the target contract spends WETH partially, the remaining WETH is transferred to the `from` address incorrectly. 
```solidity
File: DecentBridgeExecutor.sol
24:     function _executeWeth(
25:
``UTBExector`` contract tokens balance could be fully drained
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L134-L162
https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L108-L124
https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L311-L319


# Vulnerability details

## Impact
A high-severity bug has been identified within the contract ``UTB``'s private function ``_swapAndExecute``:
```
    function _swapAndExecute(
        SwapInstructions memory swapInstructions,
        address target,
        address paymentOperator,
        bytes memory payload,
        address payable refund
    ) private {
        (address tokenOut, uint256 amountOut) = performSwap(swapInstructions);
        if (tokenOut == address(0)) { // in case is native token
            executor.execute{value: amountOut}(
                target,            //
                paymentOperator,   //
                payload,           // @audit --> unchecked variables
                tokenOut,           //
                amountOut,          //
                refund             // 
            );
        } else {
            IERC20(tokenOut).approve(address(executor), amountOut);
            executor.execute(
                target,                
                paymentOperator,       // @audit --> same thing as above
                payload,
                tokenOut,
                amountOut,
                refund
            );
        }
    }
```
The vulnerability arises from inadequate input validation in the functions calling [_swapAndExecute](https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L134 ), specifically [swapAndExecute](https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L108 ) and [receiveFromBridge](https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L311 ). The unchecked inputs are subsequently utilized in the ``UTBExecutor`` contract's [execute](https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTBExecutor.sol#L41 ) function, posing a significant risk.

An attacker could exploit this flaw to execute arbitrary commands, potentially leading to the unauthorized transfer of ERC20 tokens from the executor contract balance.

## Proof of Concept
1) Use a manipulated input with a crafted payload(like transfer(address,uint256) using params as attacker address and executor contract balance as the uint256) and a target that is the erc20 selected for the first token drain
2) call one of the two function affected (``receiveFromBridge`` or ``swapAndExecute``) with the manipulated input crafted above
3) repeat the process with all the erc20 tokens present in the ``UTBExecutor`` that could be immediately sold after that resulting in profit for the attacker and loss of fu
Users can avoid paying fees because of missing input validation
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L108-L115
https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L228-L249


# Vulnerability details

## Impact

According to the project's documentation, there are different fees for the operations on different chains. From the project [docs](https://docs.decent.xyz/docs/key-concepts#fees).

```
The Box charges 0.00044 ETH on Arbitrum, Base, Zora, Optimism, Avalanche, Moonbeam, and Fantom, 0.00077 ETH on Ethereum, and 0.81 MATIC on Polygon for transactions that require a bridge or a swap. There are no fees on direct transactions.
```

The `swapAndExecute` function in `UTB.sol` takes a `FeeStructure` input parameter and has the `retrieveAndCollectFees` modifier that is responsible for transferring the fees from the sender to the `UTB` contract and then to the `FeeCollector`.

```solidity
    modifier retrieveAndCollectFees(
        FeeStructure calldata fees,
        bytes memory packedInfo,
        bytes calldata signature
    ) { //@audit it doesn't check anywhere if fees are not set to 0
        if (address(feeCollector) != address(0)) {
            uint value = 0;
            if (fees.feeToken != address(0)) {
                IERC20(fees.feeToken).transferFrom(
                    msg.sender,
                    address(this),
                    fees.feeAmount
                );
                IERC20(fees.feeToken).approve(
                    address(feeCollector),
                    fees.feeAmount
                );
            } else {
                value = fees.feeAmount; 
            }
            feeCollector.collectFees{value: value}(fees, packedInfo, signature);
        }
        _;
    }
```

However, there is no input validation at all meaning the user can pass the smallest possible amount (1 wei) or even 0 if the ERC20 token that is used doesn't revert on zero transfers. Thus, the users can avoid paying any fees without any pre-conditions and this will lead to loss of funds for the protocol. This means that both the impact and the likelihood are high.

## Proof of Concept
1. User A calls `swapAndExecute` with the following `FeeStructure`:
```solidity
    uint bridgeFee = 1 wei;
    address feeToken = any ERC20 token;
    uint feeAmount = 1 wei;
```
2. The modifier is bypassed as there are no require checks and 1 wei will be transferred.
3. The `swapAndExecute` call continues even though the sender didn't pay the necessary fees mentioned in the docs.

## Tools Used

Manual Review

## Recommended Mitigation Steps

Add appropriate checks to verify that the sender actually sent the necessary amount of fees.





## Assessed type

Invalid Validation
An attacker can hijack any ERC777 tokens flowing through the protocol and cause DoS of any operation involving ERC777 tokens
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/main/src/UTBExecutor.sol#L41


# Vulnerability details

## Prerequisite knowledge
**ERC777 Hooks**: ERC777 hooks act as callback functions that are executed whether a token owner is trying to send ERC777 tokens or a token receiver is receiving ERC777 tokens. ERC20 tokens just update balances during transfers. But ERC777 tokens do it this way:
1. Make a hook call to an address chosen by the token sender.
2. Update balances.
3. Make a hook call to an address chosen by the token receiver.

    ![ERC777 Hooks](https://thumb.tildacdn.com/tild3138-6331-4330-b666-663033316335/-/format/webp/image_1.png)

**Registry and implementers:** All ERC777 tokens are linked to the registry contract https://etherscan.io/address/0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24. This address is used by ERC777 tokens to store preferred hook receivers. These preffered hook receivers are called "interface implementers".

It means that Alice can choose Bob as her interface implementer. Bob will receive hooks if Alice receives or sends ERC777 tokens.

Alice can manage different hook types. So, she can choose Bob as an interface implementer as long as Alice sends tokens and she can choose Tom only when Alice receives tokens.

In the majority of cases, she can also choose different interface implementers for different tokens.

For mofe info on the details of ERC777 and it's features, please check: https://ethereum.org/developers/docs/standards/tokens/erc-777


## Overview of the `UTBExecutor.sol` contract

The [`UTBExecutor`](https://github.com/code-423n4/2024-01-decent/blob/main/src/UTBExecutor.sol) contract is the contract responsible for executing any arbitrary calls in the project. There are two ways it can get called.

**Way #1**: The user calls the [`UTB::swapAndExecute()`](https://github.com/code-423n4/2024-01-decent/blob/main/src/UTB.sol#L108) function. The [`UTB::swapAndExecute()`](https://github.com/code-423n4/2024-01-decent/blob/main/src/UTB.sol#L108) function is one of the entry points of this protocol. It simply conducts a swap operation then executes an arbitrary call all on the same chain.

*Example of it's usage*: The user, has 1,000 USDC and he wants to mint an NFT in the same chain which costs 1000 DAI. The user will call the [`UTB::swapAndExecute()`](https://github.com/code-423n4/2024-01-decent/blob/main/src/UTB.sol#L108) function which will in turn, swap the user's 1,000 USDC with 1,000 DAI through uniswap and then execute an arbitrary call which would mint the NFT for the 1,000 DAI. The user gets to specify the `target` of the arbitrary call as well as the calldata he sends to it, as well as specify the details of the swap operation (address of tokenIn, address of tokenOut, amount of tokenIn, and amount of tokenOut).

**Way #1**: The user calls the [`UTB::bridgeAndExecute()`](https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L259) function. T
bridgeAndExecute() and swapAndExecute() can be successfully called by attackers as long as the instructions and fees are not changed because of the signature replay and signature malleability of ecrecover() in collectFees()
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/main/src/UTBFeeCollector.sol#L53
https://github.com/code-423n4/2024-01-decent/blob/main/src/UTB.sol#L248
https://github.com/code-423n4/2024-01-decent/blob/main/src/UTB.sol#L266
https://github.com/code-423n4/2024-01-decent/blob/main/src/UTB.sol#L115


# Vulnerability details

## Impact
Attackers can call `bridgeAndExecute()` and `swapAndExecute()` successfully by replaying previously used signatures signed by the `signer` as long as the instructions and fees are not changed. 

Also because of the signature malleability vulnerability in ecrecover() attackers can create another valid signature from a previously used signature signed by the `signer`

## Proof of Concept

collectFees() in UTBFeeCollector.sol is called by retrieveAndCollectFees() modifier in UTB.sol and the modifier is being used in `bridgeAndExecute()` and `swapAndExecute()`

Attackers can replay used signatures signed by the `signer` before and call those functions successfully as long as the instructions and fees are not changed, as no check for used signatures is present in collectFees()

Also, collectFees() is using ecrecover() directly without using checking for signature malleability. Attackers can create another valid signature from a previously used signature that when passed to ecrecover() will return the `signer` address, which will pass the signature check in collectFees()


## Tools Used
Manual review

## Recommended Mitigation Steps
Use the Openzeppelin ECDSA library instead of using ecrecover() directly, and check if the signature has been used.











## Assessed type

Other