xanoutas/solidity-security-findings huggingface dataset:
No Check for '0' feeAmount
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Potential Denial of Service (DoS) Attack with FeeAmount Check

## Proof of Concept
https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L108

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

In the code at line number 236 in the UTB.sol, there is a modifier that does not checks if the FeeAmount is 0. This condition, if not handled properly, can be exploited to perform a Denial of Service (DoS) attack by causing the affected functionality to be repeatedly called without incurring any fees.

If the FeeAmount is set to 0, the modifier will always fail, preventing the execution of the intended functionality.

## Recommended Mitigation Steps
It is recommended to review the logic in the https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L228 modifier and ensure that a FeeAmount of 0 is handled appropriately to prevent potential DoS attacks.
Add this on Line #233
```solidity
require(feeCollector.FeeAmount > 0, "FeeAmount must be greater than 0");






## Assessed type

DoS
Replay attacks using the same signature are possible due to missing nonce in constructedHash
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Currently the feesStructure provided by the user through function swapAndExecute()/bridgeAndExecute() is verified offchain. When the call is sent onchain, a signature is provided which ensures that the fees are verified by the signer. This signer is obtained through the ecrecover function in function collectFees().

The issue with the current code is that the constructedHash does not follow the EIP-712 spec due to a missing nonce variable in the constructedHash as well as the fee and instruction structs only being encoded in swapAndExecute() and not hashed. Due to this, the same signature can be replayed even if the fees change through the offchain verification system. This could be used by the attacker to pay less fees when the offchain verification system now demands more. Note: the chainId should be included as well to avoid cross-chain replay attacks.

## Proof of Concept

As we can see, the constructedHash does not include a nonce, which allows this issue to occur.
```solidity
File: UTBFeeCollector.sol
45:     function collectFees(
46:         FeeStructure calldata fees,
47:         bytes memory packedInfo,
48:         bytes memory signature
49:     ) public payable onlyUtb {
50:         bytes32 constructedHash = keccak256(
51:             abi.encodePacked(BANNER, keccak256(packedInfo))
52:         );
53:         (bytes32 r, bytes32 s, uint8 v) = splitSignature(signature);
54:         address recovered = ecrecover(constructedHash, v, r, s);
55:         require(recovered == signer, "Wrong signature");
56:         if (fees.feeToken != address(0)) {
57:             IERC20(fees.feeToken).transferFrom(
58:                 utb,
59:                 address(this),
60:                 fees.feeAmount
61:             );
62:         }
63:     }
```
Additionally, in the swapAndExecute() function, we can see that the structs instructions and fees are abi encoded but not hashed individually.
```solidity
File: UTB.sol
110:     function swapAndExecute(
111:         SwapAndExecuteInstructions calldata instructions,
112:         FeeStructure calldata fees,
113:         bytes calldata signature
114:     )
115:         public
116:         payable
117:         retrieveAndCollectFees(fees, abi.encode(instructions, fees), signature)
118:     {
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider adding a nonce and chainId to the constructedHash.


## Assessed type

Error
The tokens can be swapped without paying any fees, this will be unexpected to the protocol loosing their fee revenue
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/main/src/swappers/UniSwapper.sol#L143
https://github.com/code-423n4/2024-01-decent/blob/main/src/swappers/UniSwapper.sol#L100
https://github.com/code-423n4/2024-01-decent/blob/main/src/swappers/UniSwapper.sol#L123


# Vulnerability details

## Impact
The user don't have to pay any fees to swap their tokens.

## Proof of Concept
The `UTB.sol/performSwap()` calls `UniSwapper.sol/swap()` which then calls the functions `swapExactIn`, `swapExactOut` and `swapNoPath` according to the nature of params.
But these three functions are public and not have any access control which makes them to be used by anyone. This then can be used by all the users to swap the tokens. Moreover these functions don't impose any fee to the user which will incentivise the user to call these functions and not the UTB functions to just swap  their tokens.

## Tools Used
Manual Review

## Recommended Mitigation Steps/
Considering setting the visibility of these functions `swapExactIn`, `swapExactOut` and `swapNoPath` to internal since these are not called outside the contract itself.


## Assessed type

Other
Nonces not used in signed data in contract UTBFeeCollector.sol 
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/07ef78215e3d246d47a410651906287c6acec3ef/src/UTBFeeCollector.sol#L44-L63


# Vulnerability details

## Impact
Nonces not used in signed data in contract `UTBFeecollector.sol` and function `collectFees` which is possible to signature replay attack. 


## Proof of Concept

the signed data must contain `nonce` value so block the chance of sig reply attack.
```soildity    
    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
            );
        }
    }
``` 
this function also used in many of functions which gives users to give input for this function and signature.
## Tools Used
vs code
## Recommended Mitigation Steps
- Include a nonce in what is signed 




## Assessed type

Other
Funds can be permanentley stuck in StargateAdapter
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/swappers/UniSwapper.sol#L138
https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/swappers/UniSwapper.sol#L159


# Vulnerability details

## Impact

If the price changes enough the post-bridge swap will revert due to the slippage protection. Funds will not be refunded but instead stay in StarGateBridgeAdatper. 

The swap can be attempted again by calling `clearCachedSwap()` on stargate Router  but since the swap parameter can not be updated funds can be trapped for a long period of time or potentially stuck forever if the price never returns to a level that matches the initial parameters.

## Proof of Concept

Steps showing `sgReceive()` can revert and leave funds in StargateBridgeAdapter:

Before `sgReceive()` is called it receives the bridged funds. 

`sgReceive()` calls `IUTB(utb).receiveFromBridge()` where a swap is attempted.[L209](https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/bridge_adapters/StargateBridgeAdapter.sol#L209-L216)

```solidity
_swapAndExecute(postBridge, target, paymentOperator, payload, refund);
```

Where `swap()` [L95](https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L95) is called on UniSwapper to subsequently call Univ3Router to either swap for exact input or exact output. 

If we are swapping for exact input the call looks like this [L138](https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/swappers/UniSwapper.sol#L138)

```solidity
amountOut = IV3SwapRouter(uniswap_router).exactInput(params);
```

which revert on these conditions [L28](https://github.com/Uniswap/swap-router-contracts/blob/550c0f20373a487996fcc957075377b67af9df07/contracts/V3SwapRouter.sol#L167C9-L167C78)
```solidity
require(amountOut >= params.amountOutMinimum, 'Too little received');
```

If the slippage is high enough this transaction reverts and the funds are left in StargateBridgeAdapter.

If the price never recovers all subsequent calls to `sgReceive()` with `clearCachedSwap()` will also fail.

## Tools Used
vscode, 
## Recommended Mitigation Steps
Catch the revert and refund the user on the destination chain if the a swap fails.





## Assessed type

Uniswap
UTBExecutor is not decreasing allowance when it is already set a non-zero value
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Proof of Concept
Non-standard tokens like USDT will cause the transaction to revert when a contract or user attempts to approve an allowance while the spender allowance is already set to a non-zero value. However, this consideration is not reflected in `UTBExecutor`. As evident here, [`UTBExecutor` approves `paymentOperator` with `amount` each time the execute function is called from `UTB`](https://github.com/code-423n4/2024-01-decent/blob/main/src/UTBExecutor.sol#L62). As noted by the sponsor in the Discord channel:

- payment operator typically gets used in the case of secondary marketplace orders, i.e. seaport... where essentially, lets say I need to execute a transaction to a specific contract address, but i need to make the erc20 approval to an address other than that contract. believe seaport uses this to handle all the royalty payouts etc, so you approve the payment operator, and then call the target address... in most cases payment operator and target would be the same address

While `paymentOperator` is mostly equal to `target`, it is crucial to note that `paymentOperator` might not always be equal to target. Additionally, `paymentOperator` might not use its allowance from `UTBExecutor` [(as the post-call balance is checked to refund any remaining amounts)](https://github.com/code-423n4/2024-01-decent/blob/main/src/UTBExecutor.sol#L73-L80). This implies that on the subsequent call of `UTBExecutor::execute`, the result of `USDT/NON-StandardToken::allowance(UTBExecutor, paymentOperator)` might already be greater than 0. This situation leads to the [`UTBExecutor::execute` being reverted while attempting to approve `paymentOperator` again](https://github.com/code-423n4/2024-01-decent/blob/main/src/UTBExecutor.sol#L62). also there are no other ways to rest this allowance to 0, if this issue happens.

## Impact
unfortunately, i can not determine an accurate impact for this issue because we know that this `"revert approval > 0 if already approved > 0"` is only true for some non-standard tokens like `USDT` on ethereum mainnet, and its not clear whether `USDT` or a non-standard token is going to be used in `UTB` frequently or not. However, let's assume that [`USDT` is used frequently in `UTBExecutor`](https://github.com/code-423n4/2024-01-decent/blob/main/src/UTBExecutor.sol#L45). and that `paymentOperator` is a contract that is frequently called from `UTBExecutor`. Now, if `USDT::allowance(paymentOperator, UTBExecutor)` is more than 0, then all the subsequent calls to [UTBExecutor with USDT as the token parameter and `paymentOperator` will be reverted](https://github.com/code-423n4/2024-01-decent/blob/main/src/UTBExecutor.sol#L62) with no way to revoke that approval.

## Tools Used
Manual review

## Recommended Mitigation Steps
The fix for this issue is very simple, we only need to rest the approval to zero, before approv
Potential re-entrancy attack in the execute function
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The `execute` function allows the execution of arbitrary external calls. In the case of native transfers `token == address(0)`, there is a potential re-entrancy vulnerability.


## Proof of Concept
The contract first performs an external call and then transfers native tokens, and after that changing the state. An attacker could deploy a malicious contract to re-enter the execute function during the native transfer, potentially leading to unexpected behaviour.
```solidity
    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 Review

## Recommended Mitigation Steps
Consider following the Checks-Effects-Interactions pattern, where external calls are performed after state changes.


## Assessed type

Reentrancy
Not Returning Extra ETH
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
In `performSwap` function of `UTB.sol` there is a risk of ETH getting unintentionally locked in the contract. When a user sends more ETH than the required amount for the swap (`swapParams.amountIn`). The excess ETH is not refunded or used, leading to a situation where user funds are irretrievably stuck in the contract.

## Proof of Concept
```solidity
function performSwap(
        SwapInstructions memory swapInstructions,
        bool retrieveTokenIn
    ) private returns (address tokenOut, uint256 amountOut) {
        ISwapper swapper = ISwapper(swappers[swapInstructions.swapperId]);

        SwapParams memory swapParams = abi.decode(
            swapInstructions.swapPayload,
            (SwapParams)
        );

        if (swapParams.tokenIn == address(0)) {
            require(msg.value >= swapParams.amountIn, "not enough native"); //@audit return extra amount
            wrapped.deposit{value: swapParams.amountIn}();
            swapParams.tokenIn = address(wrapped);
            swapInstructions.swapPayload = swapper.updateSwapParams(
                swapParams,
                swapInstructions.swapPayload 
            );
        } else if (retrieveTokenIn) {
            IERC20(swapParams.tokenIn).transferFrom(
                msg.sender,
                address(this),
                swapParams.amountIn
            );
        }
```
- The function checks if `tokenIn` is zero (representing native ETH) and requires that `msg.value >= swapParams.amountIn`.
- If `msg.value` is greater than `swapParams.amountIn`, the excess ETH is not handled and it will be stuck into the contract so user will loose their funds unintentionally


## Tools Used
- Manual Code Review

## Recommended Mitigation Steps
- **Refund Excess ETH**: Modify the `performSwap` function to include a mechanism that refunds any excess ETH to the `msg.sender`. 


## Assessed type

ETH-Transfer
Fee on transfer tokens can cause issues
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The current implementation doesn't work with fee-on-transfer underlying tokens. Considering that for instance USDT can activate fees, this issue can affect the protocol.
As USDT is one of the main pools on Stargate: https://stargateprotocol.gitbook.io/stargate/developers/contract-addresses/mainnet, this can have a direct impact on the protocol.

## Proof of Concept

This basically affects everywhere the `transfer` and `transferFrom` functions are used.
As the protocol expects to receive the same amount of tokens as it being specified in the `transfer` / `transferFrom`.
One such example if pulling a certain amount of tokens from a user: https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L83-L85.
Thsi will not work as the UniSwapper also pulls these tokens and expects to receive `swapParams.amountIn`. 

## Tools Used
- Manual review

## Recommended Mitigation Steps

To support these types of tokens one way is to record the balances before and after and not rely on the amounts passed to the `transfer` / `transferFrom` functions.


## Assessed type

Token-Transfer
Hardcoding stargate fee can cause the transaction to permanently revert
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact

Hardcoding the stargate fee to calculate the minimum amount out is problematic due to 2 reasons:
- Stargate can update the fee library at any time which will break the contract
- Even if the fee library is not updated, the fee can be higher than the hardcoded value and the transaction will revert

As this is the main functionality of the system and hardcoding fee can cause the system to break, but there is no risk of losing funds, I would classify this as a medium risk.

## Proof of Concept

Stargate's swap interface lets the caller specify the minimum amount of tokens he wants to receive on another chain: https://github.com/stargate-protocol/stargate/blob/main/contracts/Router.sol#L107-L133.
Each pool has a variable fee calculated by the fee library: https://github.com/stargate-protocol/stargate/blob/main/contracts/Pool.sol#L164-L212
The fee library can be freely set at any time: https://github.com/stargate-protocol/stargate/blob/main/contracts/Pool.sol#L413.
The `StargateBridgeAdapter` might work with the hardcoded fee value for the exisiting fee library, but it will break if the fee library is updated to have different logic.

As a consequence hardcoding minAmountOut doesn't work as the fee can be higher than the hardcoded value and the transaction will revert.
if we look at the currently deployed FeeLibrary it is version 7: https://stargateprotocol.gitbook.io/stargate/developers/contract-addresses/mainnet

## Tools Used
- Manual review

## Recommended Mitigation Steps
Allow the user to specify the minimum amount out and don't hardcode it.


## Assessed type

Other
Permanent loss of tokens if swap data gets outdated
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
While sending funds through `StargateBridgeAdapter`, the user passes the swap data as a parameter. The flow is stargate sending tokens on the receiving chain into the `StargateBridgeAdapter` and executing `sgReceive`.

The issue here is that `sgReceive` will fail if the swap data gets outdated, but this is not going to make the whole transaction revert.

Stargate will still send the tokens to the `StargateBridgeAdapter`, and if the `sgReceive` fails, the swap will be cached for later execution. 
See `StargateComposer` logic here: https://stargateprotocol.gitbook.io/stargate/stargate-composability/stargatecomposer.sol#sgreceive.

Now, tokens will be left sitting in the `StargateBridgeAdapter` contract, and since the user can only retry the transaction with the same swap data, the tokens will be stuck forever.

The impact is loss of transferred tokens for the user. 

## Proof of Concept

Consider the following flow:
1. User invokes `StargateBridgeAdapter:bridge()` function as part of the whole flow of calling `UTB:bridgeAndExecute()` function.
2. He is passing the swapData parameters to the remote chain. 
3. `StargateComposer` first transfers the tokens to the `StargateBridgeAdapter` contract. 
4. After that the `StargateBridgeAdapter:sgReceive()` function is executed.
5. It then calls `UTB:receiveFromBridge` that tries to execute the swap.
6. If the swap fails, which can frequently occur due to the fact that the swap data is outdated, most commonly minimum amount out gets outdated.
7. The whole payload gets stored inside the `StargateComposer` contract, and the tokens are left in the `StargateBridgeAdapter` contract.
8. As the user can only retry the transaction with the same payload, the tokens will be stuck forever.
9. An example of StargateComposer contract can be seen on: https://stargateprotocol.gitbook.io/stargate/developers/contract-addresses/mainnet#ethereum

Note: The application needs to use the `StargateComposer` contract as opposed to StargateRouter because it is transferring funds with payload, i.e. it executes `sgReceive` on the receiving chain.
You can  only use `StargateRouter` directly if you're sending funds empty payload. 
More on it: https://stargateprotocol.gitbook.io/stargate/stargate-composability

## Tools Used
- Manual review

## Recommended Mitigation Steps
Wrap the whole `StargateBridgeAdapter:receiveFromBridge()` call into a try/catch and if it reverts send the transferred tokens back to the user.


## Assessed type

Other