xanoutas/solidity-security-findings huggingface dataset:
Distributable tokens with a decimal less than LiquidInfrastructureERC20's decimal (which is 18) can never be distributed.
Audit: 2024-02-althea-liquid-infrastructure-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/main/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L275


# Vulnerability details

## Impact
For the distributable ERC20 tokens with a decimal less than LiquidInfrastructureERC20's decimal, entitlement per unit could be zero in the `_beginDistribution` function.

```
        uint256 supply = this.totalSupply();
        for (uint i = 0; i < distributableERC20s.length; i++) {
            uint256 balance = IERC20(distributableERC20s[i]).balanceOf(
                address(this)
            );
            uint256 entitlement = balance / supply;
            erc20EntitlementPerUnit.push(entitlement);
        }
```

So for the such tokens, 0 could be pushed as an erc20EntitlementPerUnit.
In the `distribute()` function, the entitlement to be transferred is calculated based on the `erc20EntitlementPerUnit` so the amount will be zero.
```
        for (uint j = 0; j < distributableERC20s.length; j++) {
            IERC20 toDistribute = IERC20(distributableERC20s[j]);
            uint256 entitlement = erc20EntitlementPerUnit[j] *
                this.balanceOf(recipient);
            if (toDistribute.transfer(recipient, entitlement)) {
                receipts[j] = entitlement;
            }
        }
```
As a result, nothing will be transferred and the tokens will be locked forever.

## Proof of Concept
https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/main/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L275

```
function test_distribution() public {
        DistributableToken distributableERC20 = new DistributableToken(); // DistributableToken's decimal is 8
        address user = address(2);
        address[] memory managedNFTs;
        address[] memory users = new address[](1);
        users[0] = user;
        address[] memory distributableErc20s = new address[](1);
        distributableErc20s[0] = address(distributableERC20);
        LiquidInfrastructureERC20 liquidInfrastructureERC20 = new LiquidInfrastructureERC20("LiquidInfrastructureERC20", "LIE", managedNFTs, users, 0, distributableErc20s); // LiquidInfrastructureERC20's decimal is 18
        // Mints 100 distributableERC20 tokens to the liquidInfrastructureERC20 token address
        distributableERC20.mint(address(liquidInfrastructureERC20), 100e8);
        assertEq(distributableERC20.balanceOf(address(liquidInfrastructureERC20)), 100e8);
        // Mints 10 liquidInfrastructureERC20 tokens to the user
        liquidInfrastructureERC20.mint(user, 10e18);
        // All the 100 distributableERC20 tokens should be distributed to the user
        liquidInfrastructureERC20.distributeToAllHolders();
        assertEq(distributableERC20.balanceOf(user), 0); // But nothing is distributed
        assertEq(distributableERC20.balanceOf(address(liquidInfrastructureERC20)), 100e8); // 100 distributableERC20 tokens is still in the liquidInfrastructureERC20 token address
withdrawFromManagedNFTs can be made to revert by a malicious owner of LiquidInfrastructureNFT
Audit: 2024-02-althea-liquid-infrastructure-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L376-L378


# Vulnerability details

## Impact
withdrawFromManagedNFTs can be made to revert by a malicious owner of LiquidInfrastructureNFT. The malicious owner can insert any address in the `thresholdErc20s` using the `setThresholds` function. Doing so can make the withdrawFromManagedNFTs function revert.

## Proof of Concept
The protocol decides to add a `LiquidInfrastructureNFT` to the LiquidInfrastructureERC20. Hence, decides to transfer it first to the LiquidInfrastructureERC20 before calling the addManagedNFT function. But, a malicious owner of LiquidInfrastructureNFT can call the setThresholds function and add a malicious contract address to the list of `thresholdErc20s` that always reverts. The malicious owner would do this just before transferring the NFT to the LiquidInfrastructureERC20 contract.

Now, when the withdrawFromManagedNFTs function is called, it would loop through the thresholdErc20s of the LiquidInfrastructureNFT contract. It would eventually call the transfer function on the malicious address that was originally set by the malicious owner, causing the function to revert.

Also, the protocol assumes that thresholdErc20s will be the same as distributableERC20s. This assumption is not right.

```
    (address[] memory withdrawERC20s, ) = withdrawFrom.getThresholds();
     withdrawFrom.withdrawBalancesTo(withdrawERC20s, address(this));
     emit Withdrawal(address(withdrawFrom));
```

withdrawBalancesTo for a malicious address would revert.

## Tools Used
Manual review

## Recommended Mitigation Steps
Instead of querying for the `withdrawERC20s` using the `getThresholds` function as seen above in the code snippet, the LiquidInfrastructureERC20 contract should transfer the balances of `distributableERC20s` to itself, as only the `distributableERC20s` are relevant to it.


## Assessed type

Other
Approved Holders will not be added to the holders list even though they are licensed to receive rewards they will get none.
Audit: 2024-02-althea-liquid-infrastructure-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L106-L108


# Vulnerability details

## Impact
Even though an account gets licensed to hold the ERC20 token, token can be minted to the account yet the address receives zero reward since they are not added to the list of holders. 

## Recommended Mitigation Steps
Approved accounts holders should be pushed into the the array of holders immediately they are approved.


## Assessed type

Other
LiquidInfrastructureERC20 decimals are not taking into account when performing calculations of entitlement units
Audit: 2024-02-althea-liquid-infrastructure-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L222-L223
https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L275-L276


# Vulnerability details

The entitlementPerUnit is calculated based on LiquidInfraERC20 totalSupply and the balance of tokens ( e.g: USDC ), that the contract is holding. The default decimals  used for LiquidInfraERC20 is 18 ( can be observed in the ERC20 contract that is inherited by LiquidInfraERC20 ), if the contract owner is taking into consideration  the tokens decimals when is minting the tokens/shares (as he should, otherwise they will not be able to use them properly in most of the exchanges/other protocols), then the contract will be blocked when trying to transfer tokens that have less then 18 decimals.

## Impact
DOS on distribution of tokens if it will try to send as rewards tokens that have less then 18 decimals ( e.g: USDC )

## Proof of Concept
1. LiquidInfraERC20 received 1 USDC as rewards from one of the nft it is holding.
2. Admin have minted the LiquidInfraERC20 based on the number of decimals the ERC20 have, so let's say it is having 1e18 tokens that are owned by one holder.
3. When contract logic will try to calculate the [entitlementPerUnit](https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L275-L276) it will divide the balance of the USDC that the contact is holding to the totalSupply of LiquiInfraERC20 that have been minted,  resulting in a something like this --> 1e6/1e18 which will return 0, and the value 0 will be saved in the [entitlementPerUnit array](https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L276C13-L276C36)
4. Then when the contract will [calculate how many tokens it will need to send to the LiquidInfraERC20 holder](https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L220-L227) it will result in 0 tokens, and from case to case ( ERC20 to ERC20 ) it can revert when trying to send 0 tokens or it will just simply operate a 0 tokens transfer, but in any case you choose the holders will receive 0 rewards of that token ( e.g: USDC) or no tokens at all if it will revert because of a different ERC20 that does not allow 0 transfers and reverts.

## Tools Used
Manual Review

## Recommended Mitigation Steps
There are multiple solutions: 
1. Create specific LiquidInfraERC20 for specific tokens, like LiquidInfraUSDC, LiquidInfraUSDT that will follow the decimals of the unde
Next batch of number of token distributions cannot be distributed
Audit: 2024-02-althea-liquid-infrastructure-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L222


# Vulnerability details

## Impact
When $distribute$ function is called it first fetches how much each token is entitled to by doing this 
https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L276
```
   for (uint i = 0; i < distributableERC20s.length; i++) {
            uint256 balance = IERC20(distributableERC20s[i]).balanceOf(
                address(this)
            );
            uint256 entitlement = balance / supply;
            erc20EntitlementPerUnit.push(entitlement);
```
This is where the problem lies because before the entitlement is pushed, because in here the previous entitlement are cleared by using `delete`
https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L266
So this will work for the first batch but for the next time it will, not work since the previous entitlement are just made 0 when we use `delete`, so when we calculate how much token to transfer to each holder for a particular token, it will just have 0 value for most of the index  
```
uint256 entitlement = erc20EntitlementPerUnit[j] *
                        this.balanceOf(recipient);
```
So when the entitlement amount is transferred to the recipient 
https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L224

## Proof of Concept
```
 uint i;
        for (i = nextDistributionRecipient; i < limit; i++) {
            address recipient = holders[i];
            if (isApprovedHolder(recipient)) {
                uint256[] memory receipts = new uint256[](
                    distributableERC20s.length
                );
                for (uint j = 0; j < distributableERC20s.length; j++) {
                    IERC20 toDistribute = IERC20(distributableERC20s[j]);
                    uint256 entitlement = erc20EntitlementPerUnit[j] *
                        this.balanceOf(recipient);//@audit right now the 0th token is 0
                    if (toDistribute.transfer(recipient, entitlement)) {
                        receipts[j] = entitlement;
                    }
                }
```


## Recommended Mitigation Steps
Maybe use a different logic for clearing out the entitlements or use a mapping for a particular address to uint for a token


## Assessed type

Invalid Validation
Rounding Issue Causes Zero Token Distribution Despite Positive Balance
Audit: 2024-02-althea-liquid-infrastructure-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L275-L276


# Vulnerability details

## Impact
The impact of the Rounding issue in the `_beginDistribution` function is that the `LiquidInfrastructureERC20.sol` contract may fail to distribute any of the held USDC or USDT (or any other ERC20 tokens with fewer decimals than the `LiquidInfrastructureERC20` token) to its token holders. This is due to the division operation resulting in zero when the balance of the ERC20 token is divided by the `totalSupply` of the `LiquidInfrastructureERC20` token, which has a higher decimal count. As a result, even if the contract holds a balance of these tokens, the calculated `entitlement` per `LiquidInfrastructureERC20` token would be zero, leading to no distribution.

## Proof of Concept

The `LiquidInfrastructureERC20` contract has a `totalSupply` of 100 tokens (with 18 decimals).
The contract holds a balance of 50 USDC or USDT (with 6 decimals).
A user calls the `distribute` function, which triggers `_beginDistribution`.
Inside `_beginDistribution`, the contract attempts to calculate the entitlement per LiquidInfrastructureERC20 token as 50 USDC / 100 tokens.
```javascript
  uint256 supply = this.totalSupply();
        for (uint i = 0; i < distributableERC20s.length; i++) {
            // ex, usdt usdc and weth balance which token recieved
            uint256 balance = IERC20(distributableERC20s[i]).balanceOf(
                address(this)
            );
            // @audit -> Rounding issue
            uint256 entitlement = balance / supply;
            erc20EntitlementPerUnit.push(entitlement);
        }
```
Foundry POC:
```javascript
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

import "forge-std/Test.sol";
import "../src/PrecisionLoss.sol";

contract PrecisionLossTest is Test {
    PrecisionLoss precisionLoss;

    function setUp() public {
        precisionLoss = new PrecisionLoss();
    }


    function testDivisionWithPrecisionLoss() public {
        //  a is usdc token have 1e6 decimal, b is liquiderc20 token which have 1e18 decimal
        // a have 50 usdc
        //  b the total suppy is 100 liquidERC20 token
        uint a = 50000000; // 50 * 1e6
        uint b = 100000000000000000000; // 100 * 1e18
        uint result = precisionLoss.division(a, b);
        assertEq(result, 0, "The result should be 0 due to precision loss");
    }
}
```
Result:
```javascript
    Running 1 test for test/PrecisionLoss.t.sol:PrecisionLossTest
[PASS] testDivisionWithPrecisionLoss() (gas: 7866)
Traces:
  [7866] PrecisionLossTest::testDivisionWithPrecisionLoss()
    ├─ [2551] PrecisionLoss::division(50000000 [5e7], 100000000000000000000 [1e20])
    │   └─ ← 0

Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 8.92ms
```
Due to integer division and the difference in decimal places, the result is 0, and the co
Nyxaris [H-0] Front-Running Vulnerability
Audit: 2024-02-althea-liquid-infrastructure-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/main/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L351


# Vulnerability details

## Description:

 The [`withdrawFromManagedNFTs`](https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/main/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L351) function is vulnerable to front-running attacks. In a front-running attack, a malicious actor can watch the transaction pool (mempool) for pending transactions calling [`withdrawFromManagedNFTs`](https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/main/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L351). Upon detecting such a transaction, the attacker can craft their own transaction that interacts with the `ManagedNFTs` array (e.g., by adding or removing NFTs) and submit it with a higher gas price. Because miners typically prioritize transactions with higher gas prices, the attacker's transaction may be confirmed first, altering the state of the ManagedNFTs array before the original withdrawal transaction is processed.

## Impact:
 The impact on the smart contract is high. A successful front-running attack could disrupt the intended flow of funds within the contract, leading to incorrect or unauthorized fund withdrawals. This could compromise the integrity of the fund distribution process and result in financial discrepancies within the contract's accounting.


## Tools Used
Manual Review

## Recommended Mitigation Steps
**Queue System:** Create a queuing mechanism for withdrawal requests that processes them based on the order of submission, independent of the transaction order on the blockchain.
Severity Justification: The severit


## Assessed type

Other
Unsafe usage of ERC20 transfer
Audit: 2024-02-althea-liquid-infrastructure-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L224


# Vulnerability details

*Note: This risk is extension to the another submission with the same title.*

## Impact

Some ERC20 tokens, such as USDT, do not return a boolean for functions that expect a return value. When the `transfer` function is executed for USDT tokens or any other token with a similar codebase, it expects a boolean value to confirm the success of the transfer. However, as it does not receive any truth value, the transaction will always fail.

## Proof of Concept

The `transfer` function of USDT does not return a boolean value. Therefore, even when these tokens are successfully transferred, the function will fail to execute the transaction. This susceptibility can result in DoS attacks or cause the tokens to remain trapped within the system indefinitely.

## Tools Used

Manual Review

## Recommended Mitigation Steps

Utilize OpenZeppelin's SafeERC20 contract, which includes a `safeTransfer` function. This function can be used in place of the transfer function to mitigate the identified vulnerability.



## Assessed type

Token-Transfer
Lack of Loop Termination in _afterTokenTransfer Function (Dos)
Audit: 2024-02-althea-liquid-infrastructure-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L164


# Vulnerability details

## Impact
he DoS vulnerability poses a significant risk to the availability and performance of the contract, especially in scenarios where the contract is subjected to repeated token transfers or interactions with a large number of token holders.
## Proof of Concept
The code in the _afterTokenTransfer function of the LiquidInfrastructureERC20 contract lacks proper loop termination, potentially leading to a Denial-of-Service (DoS) vulnerability. Without loop termination, the function can consume excessive gas and cause the contract to become unresponsive or expensive 


1 ,Transfer a significant amount of tokens from an address (from) to another address (to) using the transfer or similar function.
Ensure that the transferred amount results in the balance of address from becoming zero.
2.Repeat the transfer operation multiple times or transfer tokens among a large number of addresses.


The loop in the _afterTokenTransfer function continues iterating even after removing the address from from the holders array. This can result in significant  dos


https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L164C5-L179C6

    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual override {
        bool stillHolding = (this.balanceOf(from) != 0);
        if (!stillHolding) {
            for (uint i = 0; i < holders.length; i++) {
                if (holders[i] == from) {
                    // Remove the element at i by copying the last one into its place and removing the last element
                    holders[i] = holders[holders.length - 1];
                    holders.pop();
                }
            }
        }
    }

## Tools Used

Mannual


## Assessed type

DoS
A malicious user of the protocol can deny users of the protocol the ability to make maximum balance transfers.
Audit: 2024-02-althea-liquid-infrastructure-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L127
https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L164
https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L48
https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L31


# Vulnerability details

## Impact
A malicious user of the protocol can deny users of the protocol the ability to make maximum balance transfers, thereby locking a portion of all users funds permanently in the protocol. To be specific, each user will have 1 unit of token permanently locked in the protocol. Another impact is that all functions that are dependent on holders length will be permanently out of service such as [distributeToAllHolders](https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L184).

## Proof of Concept

Suppose there is USER A who is already approved by the owner to hold a Liquid Infrastructure ERC20 [token](https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L106). 

USER A holds a 1000 token units.

USER A launches an attack in the following sequence.

USER A calls [burn()](https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L31) in ERC20Burnable.

```
function burn(uint256 amount) public virtual {
        _burn(_msgSender(), amount);
}
```
However, USER A calls burn to burn 1 token unit per call with the intent of triggering the [before](https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L127) and [after](https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L164) token transfer hooks in the _burn function in base ERC20 contract.

```
function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {