xanoutas/solidity-security-findings huggingface dataset:
Attacker can block LayerZero channel due to variable gas cost of saving payload
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L197
https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L103-L109
https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L161


# Vulnerability details

## Impact
This issue is internally the same as described in https://github.com/code-423n4/2023-07-tapioca-findings/issues/1220. To avoid duplication of efforts and unnecessary copy-pasting, only the differences are explained in this issue.

## Proof of Concept
The entry point here is the [`bridgeWithPayload()`](https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L197) function in the `DecentEthRouter` contract, which allows the caller to pass a variable-length `additionalPayload` variable. This variable is then encoded as part of the `payload` variable returned by [`_getCallParams()`](https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L80) and [passed](https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L185) as parameter to `DcntEth.sendAndCall()`. This is a function that `DcntEth` inherits from `OFTV2`, which inherits it from [`BaseOFTV2`](https://github.com/LayerZero-Labs/solidity-examples/blob/ca7d4f1d482df5e17f8aaf1b34d0e4432020bc4e/contracts/token/oft/v2/BaseOFTV2.sol#L25) . This function calls the internal [`_sendAndCall()`](https://github.com/LayerZero-Labs/solidity-examples/blob/ca7d4f1d482df5e17f8aaf1b34d0e4432020bc4e/contracts/token/oft/v2/OFTCoreV2.sol#L152) function inherited from `OFTCoreV2`, which finally calls `_lzSend()`. This ties into the description of the vulnerability in the aforementioned issue.

On the other end, the message will be processed in the [`onOFTReceived()`](https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L237) function of the same contract, which is invoked in the [`OFTCoreV2.callOnOFTReceived()`](https://github.com/LayerZero-Labs/solidity-examples/blob/ca7d4f1d482df5e17f8aaf1b34d0e4432020bc4e/contracts/token/oft/v2/OFTCoreV2.sol#L48C14-L48C31) function when receiving a message. Our payload will be extracted as the [`callPayload`](https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L251) variable and passed on to the executor, where the `msgType` is hardcoded as `MT_ETH_TRANSFER_WITH_PAYLOAD` in `bridgeWithPayload()`. Finally, in  `DecentBridgeExecutor`, we [always](https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentBridgeExecutor.sol#L77-L80) get a call to our target address with the payload we defined (of which only its size matters), in which we can drain as muc
BaseOFTV2.sendFrom and sendAndCall can be called by anyone to harm the protocol
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/LayerZero-Labs/solidity-examples/blob/ca7d4f1d482df5e17f8aaf1b34d0e4432020bc4e/contracts/token/oft/v2/BaseOFTV2.sol#L15-L45


# Vulnerability details

## Impact
The Decent token is a token that inherits from [`BaseOFTV2`](https://github.com/LayerZero-Labs/solidity-examples/blob/ca7d4f1d482df5e17f8aaf1b34d0e4432020bc4e/contracts/token/oft/v2/BaseOFTV2.sol#L15-L45). There are defined two public functions - [`sendFrom and sendAndCall`](https://github.com/LayerZero-Labs/solidity-examples/blob/ca7d4f1d482df5e17f8aaf1b34d0e4432020bc4e/contracts/token/oft/v2/BaseOFTV2.sol#L15-L45). They do not have access controls and can be called by anyone. This can be used by an attacker to block a path in the cross-chain communication or use the protocol without paying fees.


## Proof of Concept
LayerZero is blocking by default. That's why [NonBlockingLzApp.sol](https://github.com/LayerZero-Labs/solidity-examples/blob/ca7d4f1d482df5e17f8aaf1b34d0e4432020bc4e/contracts/lzApp/NonblockingLzApp.sol) is used - to allow non-blocking behavior. The problem is that currently anyone can call the aforementioned functions with any parameters.

When used by the protocol, the router calls [_getCallParams](https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L80-L111) which calculates amounts of gas to be passed.

```solidity
        uint256 GAS_FOR_RELAY = 100000;
        uint256 gasAmount = GAS_FOR_RELAY + _dstGasForCall;
        adapterParams = abi.encodePacked(PT_SEND_AND_CALL, gasAmount);
```

This calculation can be entirely skipped by directly calling `sendAndCall`. An attacker can pass a very small amount of gas.

On the receiving chain, [`LzApp.lzReceive`](https://github.com/LayerZero-Labs/solidity-examples/blob/ca7d4f1d482df5e17f8aaf1b34d0e4432020bc4e/contracts/lzApp/LzApp.sol#L35-L52) will be executed which will call [`_blockingLzReceive`](https://github.com/LayerZero-Labs/solidity-examples/blob/ca7d4f1d482df5e17f8aaf1b34d0e4432020bc4e/contracts/lzApp/NonblockingLzApp.sol#L24-L39).

[`_blockingLzReceive`] will try to execute the non-blocking functionality.

```solidity
      // 63/64th of the gas will be forwarded to the call. Since the gas is not enough, success will be false
      (bool success, bytes memory reason) = address(this).excessivelySafeCall(
            gasleft(),
            150,
            abi.encodeWithSelector(this.nonblockingLzReceive.selector, _srcChainId, _srcAddress, _nonce, _payload)
        );

       // We store the failed message with the remaining 1/64th
        if (!success) {
            _storeFailedMessage(_srcChainId, _srcAddress, _nonce, _payload, reason);
        }
```

As we know, the EVM will forward 63/64th of the available gas to the internal call.
Since the passed gas is little, it will not be enough and `success` will return false. 
This will trigger the if statement below and the message will be stored as failed, leading to blocked cha
DOS on sgReceive due to lack of fund transfer.
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Vulnerability Details

Missing transferFrom in `sgReceive` of `StargateBridgeAdapter`  Normally, to spend tokens on behalf of a user, those tokens should be first transferred to the contract via transferFrom. If the Stargate protocol doesn’t automatically deposit tokens into the contract, then not having a transferFrom could be an oversight. This would mean the contract approves `utb` to spend tokens that it doesn't have, rendering the approval ineffective.

```
IERC20(swapParams.tokenIn).approve(utb, swapParams.amountIn);
```
Making approve here without the transferFrom .

the functionality should be like `    function receiveFromBridge()
` in `DecentBridgeAdapter.sol` .



## Impact
It will revert the while transaction of the call `receiveFromBridge` of `UTB`

## Proof of Concept

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


## Tools Used
Manual Review

## Recommended Mitigation Steps

First you need to transfer the funds to contract and then approve the utb to spend.

Add the 
```solidity
IERC20(swapParams.tokenIn).transferFrom( 
            msg.sender,
            address(this),
            swapParams.amountIn
        );
```
in `sgReceive` function.


## Assessed type

DoS
In UTB::performSwap() excess of Eth received is not refunded back
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
In the `UTB::performSwap()` function, the logic check for the msg.value to be greater than or equals to `swapParams.amountIn`. This is fine, since if the price moves after the estimates was provided to the user, the above validation checks if the funds received are sufficient to proceed with the transaction.

But, at the same time, it is equally important to refund any excess eth received which is not being done.

## Proof of Concept
```
    function performSwap(
        SwapInstructions memory swapInstructions,
        bool retrieveTokenIn
    ) private returns (address tokenOut, uint256 amountOut) {
            ...

        if (swapParams.tokenIn == address(0)) {
            require(msg.value >= swapParams.amountIn, "not enough native");
            ...
        } else if (retrieveTokenIn) {
            ...
        }
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Refund the amount back to the caller.



## Assessed type

Payable
Lack of Signature Replay Protection in collectFees Function
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
potential security vulnerability in the collectFees function of the contract. The function does not incorporate any mechanism for signature replay protection, exposing it to potential replay attacks.
## Proof of Concept
The collectFees function uses a signature to verify the authenticity of the transaction. However, it does not implement any protection against signature replay attacks. A malicious actor could potentially reuse a valid signature to replay a transaction, leading to unintended consequences.

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

    function collectFees(
        FeeStructure calldata fees,
        bytes memory packedInfo,
        bytes memory signature
    ) public payable onlyUtb {
        bytes32 constructedHash = keccak256(
            abi.encodePacked(BANNER, keccak256(packedInfo))
        );
        (bytes32 r, bytes32 s, uint8 v) = splitSignature(signature);
        address recovered = ecrecover(constructedHash, v, r, s);
        require(recovered == signer, "Wrong signature");
        if (fees.feeToken != address(0)) {
            IERC20(fees.feeToken).transferFrom(
                utb,
                address(this),
                fees.feeAmount
            );
        }
    }
## Tools Used

## Recommended Mitigation Steps

collectFees function by incorporating a nonce or another unique identifier in the signed message to prevent signature replay attacks. This ensures that each signature is only valid for a specific transaction.


## Assessed type

Invalid Validation
Function setRouter of the Contract DcntEth is not Protected
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/decentxyz/decent-bridge/blob/3460106d1cd976e0235695676a9e318f750cd4dd/src/DcntEth.sol#L20


# Vulnerability details

## Impact
An attacker could change the router in this Token to another router, allowing them to mint tokens for themselves. Consequently, the DecentEthRouter would not be able to add liquidity, or users would be unable to withdraw their tokens.

## Proof of Concept
Get the address of the Token DcntEth.
Set the address of a new router.
Mint new tokens for a user.
Set the address of the old router.
Withdraw the amount that was minted.

```javascript

    function testRouter() public {

        assertEq(router, dcntEth.router());

        dcntEth.setRouter(router2);

        console2.log("router is", router);
        //check the router
        assertEq(router2, dcntEth.router());
    }
```

## Tools Used
foundry

## Recommended Mitigation Steps
Only an admin should be able to set the router for DcntEth, add onlyOwner modifier in front of the function setRouter

````javascript
 function setRouter(address _router) public onlyOwner 
````


## Assessed type

Access Control
Unexpected approval to malicious address by UTBExecutor.sol leads to theft of tokens.
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
There is an unexpected approval of tokens to malicious address by `UTBExecutor.sol` which will lead to token theft from `UTBExecutor.sol`. This is also the violation of guidelines as given in the docs 
```
The contracts are not intended to hold on to any funds or unnecessary approvals
```

## Proof of Concept
The issue is in the code of `UTBExecutor.sol/execute()` which approves the `paymentOperator` which is specified by the external user and sends the tokens to refund address if the tokens are not withdrawn by the `paymentOperator`. 
This transfers the tokens to the refund address(user) and also gives an approval to  
`paymentOperator` which is not intended.
Since the `paymentOperator` is provided by the user, the user can maliciously steal all the funds after the swap and execute transaction.
```Solidity
        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 Review

## Recommended Mitigation Steps
Better to disapprove the `paymentOperator` after transferring to refund address.


## Assessed type

Other
Incorrect Amount Used in Destination Chain Swap 
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#L163
https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L168


# Vulnerability details

## Impact
The incorrect usage of amount in the cross-chain swap may lead to discrepancies in the actual swapped amount on the destination chain. This can result in  financial losses and  failure of cross-chain transactions.

## Proof of Concept
The `swapAndModifyPostBridge` function executes a swap before bridging and  stores the resulting `amountOut` in the `amount2Bridge` variable. It also calculates the correct `amountIn` for the destination chain swap and stores it in `newPostSwapParams.amountIn`.

The descepancy arises when the The `callBridge` function in the `StargateBridgeAdapter ` contract  responsible for initiating the destination chain swap utilizes the incorrect `amt2Bridge` value as the `amountIn` parameter instead of passing the `newPostSwapParams.amountIn`

```
 function callBridge(
        uint256 amt2Bridge,
        uint256 dstChainId,
        bytes memory bridgePayload,
        bytes calldata additionalArgs,
        address payable refund
    ) private {
        router.swap{value: msg.value}(
            getDstChainId(additionalArgs), //lzBridgeData._dstChainId, // send to LayerZero chainId
            getSrcPoolId(additionalArgs), //lzBridgeData._srcPoolId, // source pool id
            getDstPoolId(additionalArgs), //lzBridgeData._dstPoolId, // dst pool id
            refund, // refund adddress. extra gas (if any) is returned to this address
            amt2Bridge, // quantity to swap
            (amt2Bridge * (10000 - SG_FEE_BPS)) / 10000, // the min qty you would accept on the destination, fee is 6 bips
            getLzTxObj(additionalArgs), // additional gasLimit increase, airdrop, at address
            abi.encodePacked(getDestAdapter(dstChainId)),
            bridgePayload // bytes param, if you wish to send additional payload you can abi.encode() them here
        );
    }
```
The amt2Bridge is the amountOut which reflects the value in the source chain and does not account for potential bridge fee incurred during transfer. As a result there is potential discrepancies in transferred funds and potential financial losses.

## Tools Used
Manual Review
## Recommended Mitigation Steps
Replace `amt2Bridge` with `newPostSwapParams.amountIn` in the callBridge function's `router.swap` call.


## Assessed type

Error
Lack of Authorization in setRouter Function
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
security vulnerability in the setRouter function of the DcntEth contract. The setRouter function is marked as public, which means that anyone can call it and potentially change the router address, leading to unauthorized access and potential exploitation of the contract.
## Proof of Concept
 setRouter function is public and lacks proper authorization checks. This allows any address to call the function and update the router address, potentially compromising the security of the contract.

https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DcntEth.sol#L19C2-L23C1

function setRouter(address _router) public onlyOwner {
    router = _router;
}

## Tools Used

## Recommended Mitigation Steps

 setRouter function to include an authorization check, allowing only authorized addresses to modify the router variable. An example is provided above, where the onlyOwner modifier is used to restrict access to the contract owner

modifier onlyOwner() {
    require(msg.sender == owner(), "Not the contract owner");
    _;
}

function setRouter(address _router) public onlyOwner {
    router = _router;
}



## Assessed type

Invalid Validation
LayerZero pathway can be blocked due to variable gas cost of saving payload
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L185-L193


# Vulnerability details

## Impact

Due to variable cost of saving failed messages inside the [`failedMessages` storage variable](https://github.com/LayerZero-Labs/solidity-examples/blob/ca7d4f1d482df5e17f8aaf1b34d0e4432020bc4e/contracts/token/oft/v2/OFTCoreV2.sol#L221),
it is possible to block the LayerZero pathway by sending a big payload with the default amount of gas.

The impact is that the LayerZero pathway gets blocked and no other messages can be sent through it.

## Proof of Concept

Let's examine the `DecentEthRouter:bridgeWithPayload()` function:
1. It uses the `sendAndCall` function to send the payload to the receiving chain.
2. The adapterParams and the corresponding gasLimit to deliver the message are set to 100_000 plus any additional gas for the external call inside the `onOFTReceived` function.
3. If the `onOFTReceived` function fails, the messages is being saved into the `failedMessages` storage variable.
4. A malicious user can force the `onOFTReceived` function to fail, just to provoke saving the message. I'll leave the test to showcase the cost of saving the message in the `failedMessages` storage variable and emitting an event. 
5. As the cost for bigger payloads is around 131k, and all the extra gas is used in the `onOFTReceived` function, the `uint256 GAS_FOR_RELAY = 100000;` is not enough to execute all the logic.
6. If the transaction fails inside the [`_sendAndCallAck` function](https://github.com/LayerZero-Labs/solidity-examples/blob/ca7d4f1d482df5e17f8aaf1b34d0e4432020bc4e/contracts/token/oft/v2/OFTCoreV2.sol#L221) it
will result in storedPayload inside the [`Endpoint.sol`](https://github.com/LayerZero-Labs/LayerZero/blob/48c21c3921931798184367fc02d3a8132b041942/contracts/Endpoint.sol#L120-L123) and block the LayerZero pathway.

Gas costs of storing big paylaod inside the `failedMessages` storage variable and emitting an event:

```solidity
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.19;

import "forge-std/Test.sol";
import "forge-std/console.sol";

contract FailedMessagesTest is Test {

    mapping(uint16 => mapping(bytes => mapping(uint64 => bytes32))) public failedMessages;

    event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, bytes _reason);

    function setUp() public {}

    function testFMessagesGas() public {
        uint16 srcChainid = 1;
        bytes memory srcAddress = abi.encode(makeAddr("Alice"));
        uint64 nonce = 10;
        bytes memory payload = getDummyPayload(9999); // max payload size someone can send is 9999 bytes
        bytes memory reason = getDummyPayload(2);

        uint256 gasLeft = gasleft();
        _storeFailedMessage(srcChainid, srcAddress, nonce, payload, reason);
        emit log_named_uint("gas used", gasLeft - gasleft());
    }


    function _storeFailedMessage(
        uint16 _sr
IERC20`  UTBExecutor :: execute `  function not checked for return values
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/07ef78215e3d246d47a410651906287c6acec3ef/src/UTBExecutor.sol#L61
https://github.com/code-423n4/2024-01-decent/blob/07ef78215e3d246d47a410651906287c6acec3ef/src/UTBExecutor.sol#L62
https://github.com/code-423n4/2024-01-decent/blob/07ef78215e3d246d47a410651906287c6acec3ef/src/UTBExecutor.sol#L80


# Vulnerability details

## Impact
The `transferFrom()` ,`transfer ` and `approve` functions returns a `boolean` value indicating success. This parameter needs to be checked to see if the transfer has been successful.

Funds can be lost

 
## Tools Used

manual 

## Recommended Mitigation Steps

Check the success `boolean` of all `transferFrom()` `transfer ` and `approve` calls. Alternatively, use openzeppelin `SafeERC20` `safeTransferFrom()` function.


## Assessed type

ERC20
lzReceive fails on Arbitrum
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

While relaying payloads through `DecentEthRouter` the gas for relayer to submit the transaction on the receiving chain is hardcoded to [100_000](https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L96).
This was hardcoded with EVM compatible chains in mind and works fine expect for Arbitrum that has a totally different gas model than Ethereum.
Differences are summarized in the following article: https://docs.arbitrum.io/devs-how-tos/how-to-estimate-gas. 
As an example even a simple `approve` transaction requires more than 100_000 gas on Arbitrum: https://arbiscan.io/tx/0x04f97a39c6d03029518d1953226b343555ea585d2064a24a767155a548b0665e.

As this is hardcoded it will result with messages being sent to Arbitrum to always revert at the beginning of the `lzReceive`, and will result in blocked pathway between all chains and Arbitrum.

The impact is that the pathway between all chains and Arbitrum will be blocked and no messages can be sent to Arbitrum.

## Proof of Concept
DecentEthRouter hardcodes 100_00 gas for the relayer to deliver the message to remote chain. If this is invoked from some chain to Arbitrum the following happens:

1. The transaction is submitted to Arbitrum with [100_000 gas as gasLimit](https://github.com/LayerZero-Labs/LayerZero/blob/48c21c3921931798184367fc02d3a8132b041942/contracts/Endpoint.sol#L118).
2. It enters [lzReceive and reverts](https://github.com/LayerZero-Labs/solidity-examples/blob/ca7d4f1d482df5e17f8aaf1b34d0e4432020bc4e/contracts/lzApp/LzApp.sol#L35-L52).
3. This results in [storedPayload and blocking the pathway](https://github.com/LayerZero-Labs/LayerZero/blob/48c21c3921931798184367fc02d3a8132b041942/contracts/Endpoint.sol#L122) due to nonce ordering.

## Tools Used
- Manual review

## Recommended Mitigation Steps
Don't hardcode gas costs for each chain but implement a different gas model for Arbitrum.
A reference point for calculating equivalent gas costs for Arbitrum is the following: https://docs.arbitrum.io/devs-how-tos/how-to-estimate-gas.


## Assessed type

Other