xanoutas/solidity-security-findings huggingface dataset:
The dust collection strategy Heavily incentives MEV
Audit: 2024-03-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-pooltogether/blob/main/pt-v5-vault/src/PrizeVault.sol#L861-L872


# Vulnerability details

## Impact
The `prizeVault` contract implements a `dust collection strategy` which aims to minimize rounding errors, this is done by having the user send in x amount of tokens to the contract and get a rounded down amount of shares without leaving the "dust" in the `prizeVault` contract.

## Proof of Concept
This will not only result in user lost funds as the remaining dust is theoretically lost unless the user plans to make another deposit of the remaining to get a full share which in the same TX, this can also create potential MEV opportunities as any dust amount left over from the first user essentially becomes a discount for the second user as there first share will be priced at 
`price of share - dust in prizeVault contract` this will make a pay-it-forward future become an MEV strategy that every time there is some dust left there is a chance to buy shares at a discount.

https://github.com/code-423n4/2024-03-pooltogether/blob/main/pt-v5-vault/src/PrizeVault.sol#L861-L866

## Tools Used
Vscode

## Recommended Mitigation Steps
Consider storing the dust amount in a mapping to the users address instead of leaving it to the next person





## Assessed type

MEV
The `Prizevault:_twabSupplyLimit` function does not account for the yieldFeeBalance in its calculations.
Audit: 2024-03-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-pooltogether/blob/480d58b9e8611c13587f28811864aea138a0021a/pt-v5-vault/src/PrizeVault.sol#L798


# Vulnerability details

## Impact
While not yet minted, the accrued `yieldFeeBalance` is an inherent part of PrizeVault's shares(totalsupply), impacting the overall totalsupply. This is because minting the `yieldFeeBalance` as prizevault shares is inevitable (as long as yield fee recipient desires his fees) and really the only way to withdraw YieldFeeBalance. This poses a potential issue as the _twabSupplyLimit function, vital for calculating maxdeposits and maxamountout in `liquidatableBalanceOf`, omits consideration of minting yieldFeeBalance by yield fee recipient. In extreme scenarios, this oversight could render withdrawals unfeasible by yield fee recipient upon reaching the maximum total supply because it would mean minting prizevault shares exceeding TWAB limits. Which was what the `Prizevault:_twabSupplyLimit` was implemented to prevent.


## Proof of Concept
https://github.com/code-423n4/2024-03-pooltogether/blob/480d58b9e8611c13587f28811864aea138a0021a/pt-v5-vault/src/PrizeVault.sol#L380

https://github.com/code-423n4/2024-03-pooltogether/blob/480d58b9e8611c13587f28811864aea138a0021a/pt-v5-vault/src/PrizeVault.sol#L636

```
function maxDeposit(address) public view returns (uint256) {
        uint256 _totalSupply =  _maxDeposit();
        uint256 totalDebt_ = _totalDebt(_totalSupply);
        if (totalAssets() < totalDebt_) return 0;

        // the vault will never mint more than 1 share per asset, so no need to convert supply limit to assets
    >>>    uint256 twabSupplyLimit_ = _twabSupplyLimit(_totalSupply); 
        uint256 _maxDeposit;
        uint256 _latentBalance = _asset.balanceOf(address(this));
        uint256 _maxYieldVaultDeposit = yieldVault.maxDeposit(address(this));
        if (_latentBalance >= _maxYieldVaultDeposit) {
            return 0;
        } else {
            unchecked {
                _maxDeposit = _maxYieldVaultDeposit - _latentBalance;
            }
            // @audit would return invalid number number
            return twabSupplyLimit_ < _maxDeposit ? twabSupplyLimit_ : _maxDeposit;
        }
    }
```
If this is implemented and a user hypothetically decides to use the `twabSupplyLimit_` to make a deposit. It would mean TWAB controller's share supply limit is reached. So even when a yield fee recipient wants to claim Yield fee he would be unable to do so. Similar issue will arise from using maxamountout in `liquidatableBalanceOf` function in the link above.

## Tools Used

Manual

## Recommended Mitigation Steps

```
 function _twabSupplyLimit(uint256 _totalSupply) internal pure returns (uint256) {
        unchecked {
            return type(uint96).max - _totalSupply - YieldFeebalance
            
        }
    }
```








## Assessed type

DoS
Withdraw Function Lack of Ownership Verification for msg.sender's Assets
Audit: 2024-03-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-pooltogether/blob/480d58b9e8611c13587f28811864aea138a0021a/pt-v5-vault/src/PrizeVault.sol#L489
https://github.com/code-423n4/2024-03-pooltogether/blob/480d58b9e8611c13587f28811864aea138a0021a/pt-v5-vault/src/PrizeVault.sol#L907


# Vulnerability details

## Description

* In the ```PrizeVault.sol``` contract, the ```withdraw#L-489``` function currently lacks a verification mechanism to ensure that the ```msg.sender``` is the rightful owner of the asset. The function solely validates whether the contract holds a sufficient amount of the asset and performs null value checks. This vulnerability exposes the contract to a heightened risk of unauthorised access and potential loss of exceptional assets held within the contract's holdings.


* Implement a balance check for msg.sender in the ```withdraw``` function using the 'require' statement to ensure a secure initialization process.

```
PrizeVault.sol -> 


L-489 function withdraw(
        uint256 _assets,
        address _receiver,
        address _owner
    ) external returns (uint256) {
        @audit-issue require msg.sender own the assets check 
        uint256 _shares = previewWithdraw(_assets);
        _burnAndWithdraw(msg.sender, _receiver, _owner, _shares, _assets);
        return _shares;
    }
```


## Tools Used

Vscode 


## Assessed type

Access Control
`VaultPrize` lacks slippage protection on functions `deposit()`, `withdraw()`, `mint()`, `redeem()` and `depositWithPermit()`
Audit: 2024-03-pooltogether-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-pooltogether/blob/480d58b9e8611c13587f28811864aea138a0021a/pt-v5-vault/src/PrizeVault.sol#L475-L546


# Vulnerability details

## Impact
The functions `deposit()`, `withdraw()`, `mint()`, `redeem()` and `depositWithPermit()` relies on the values of `totalAssets` and totalSupply (`totalDebt` as the protocol defines it) which changes values often depending on the activity of the vault. It is reasonable that as the user sends input tokens (assets or shares) to the vault, he/she should receive the rightful amount of tokens (shares or asset) on that moment.

It also acts as a protection should there be manipulation (undiscoverd exploits) in the `totalAssets` and `totalDebt`.

## Proof of Concept

## Tools Used
Manual Review

## Recommended Mitigation Steps
Here's a `redeem` implementation example shown below.

```diff
    // @audit this is the original function which adheres with ERC4626
    function redeem(        
        uint256 _shares,
        address _receiver,
        address _owner
    ) external returns (uint256) { 
        uint256 _assets = previewRedeem(_shares);
        _burnAndWithdraw(msg.sender, _receiver, _owner, _shares, _assets);
        return _assets; 
    }

    // @audit added function with parametarized minimum assets `_minAssets` to be received by the user
++   function redeem(        
++       uint256 _shares,
++       address _receiver,
++       address _owner,
++       uint256 _minAssets
++   ) external returns (uint256) { 
++       uint256 _assets = previewRedeem(_shares);
++       if (_assets < _minAssets) revert SlippageProtected();
++
++       _burnAndWithdraw(msg.sender, _receiver, _owner, _shares, _assets);
++       return _assets; 
++   }
```
This type of implementation still adheres to ERC4626 and gives an option to have the slippage protection for users.

Implement this pattern to the rest of the functions: `deposit()`, `withdraw()`, `mint()` and `depositWithPermit()`.


## Assessed type

Other
Vulnerability in Merkle Tree Leaf Value Handling
Audit: 2024-03-taiko-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-taiko/blob/main/packages/protocol/contracts/team/airdrop/MerkleClaimable.sol#L77


# Vulnerability details

## Impact
By crafting specific inputs that exploit the 64-byte leaf value handling, an attacker could bypass the integrity check provided by the Merkle proof, resulting in unauthorized actions such as feeless player minting. This undermines the contract's security and can lead to significant financial losses or damage to the platform's reputation.


## POc
1. **Exploit Scenario**: An attacker analyzes the Merkle tree implementation and identifies that leaf nodes are processed without any checks for their length before being hashed. The attacker then crafts a transaction with a 64-byte payload that, when hashed, matches a valid leaf in the Merkle tree used by the contract.

2. **Execution**: The attacker submits this crafted transaction along with a seemingly valid Merkle proof. Due to the vulnerability, the contract fails to detect the anomaly, treating the transaction as legitimate and executing the mint operation without any fees.

#### Mitigation Strategies

1. **Pre-Hash Length Check**: Implement a check to ensure that leaf values are not exactly 64 bytes in length before hashing. This can involve adding or removing data from the leaf to alter its size before it is processed.

2. **Upgrade Contract**: Develop and deploy an upgraded version of the `MerkleClaimable` contract that includes the aforementioned check or any other robust mechanism to validate the integrity and format of input data before processing.

3. **Security Audit**: Conduct a comprehensive security audit of the contract to identify and rectify potential vulnerabilities related to data handling and Merkle tree construction.

4. **Monitoring and Alerts**: Implement monitoring tools to detect unusual patterns or transactions that may indicate an attempt to exploit this or similar vulnerabilities, coupled with an alert system to notify administrators of suspicious activities.

5. **Community Communication**: Transparently communicate with the user community about the vulnerability and the steps taken to address it, including any required actions from users, to maintain trust and security awareness.



## Assessed type

Context
L1 contract cannot receive ether because entryLock state has no effect on _inNonReentrant(). It is only effective on the nonReentrant() modifier
Audit: 2024-03-taiko-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-taiko/blob/f58384f44dbf4c6535264a472322322705133b11/packages/protocol/contracts/L1/TaikoL1.sol#L34


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
This bug denied the L1 contract from receiving ether and subsequently affect other functions.  

```
function _inNonReentrant() internal view returns (bool) {
        return _loadReentryLock() == _TRUE;
    }
```

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

```
function _send(uint256 amount) private {
        (bool ok, ) = address(L1).call{value: amount}("");
    }

function test_L1_EthDeposits() external {

        deal(Alice, 100 ether);
        vm.prank(Alice, Alice);
        _send(10);

        uint256 contractBalance = address(L1).balance;
        console.log("balance", contractBalance);

    }
```
## Tools Used
Manual Review

## Recommended Mitigation Steps
- Consider refactoring ReentryLock state and storeReentrantLock


## Assessed type

ETH-Transfer
Arbitrary data can be encoded and maliciously executed
Audit: 2024-03-taiko-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-taiko/blob/f58384f44dbf4c6535264a472322322705133b11/packages/protocol/contracts/tokenvault/ERC721Vault.sol#L77-L107


# Vulnerability details

## Impact
`onMessageInvocation` can be passed a malicious `_data` which could break protocol logic and invocate unexpected functions

## Proof of Concept
```
function onMessageInvocation(
        bytes calldata _data
    ) external payable nonReentrant whenNotPaused {
        (
            CanonicalNFT memory ctoken,
            address from,
            address to,
            uint256[] memory tokenIds
        ) = abi.decode(_data, (CanonicalNFT, address, address, uint256[]));

        IBridge.Context memory ctx = checkProcessMessageContext();

        if (to == address(0) || to == address(this)) revert VAULT_INVALID_TO();

        address token = _transferTokens(ctoken, to, tokenIds);
        to.sendEther(msg.value);

        emit TokenReceived({
            msgHash: ctx.msgHash,
            from: from,
            to: to,
            srcChainId: ctx.srcChainId,
            ctoken: ctoken.addr,
            token: token,
            tokenIds: tokenIds,
            amounts: new uint256[](tokenIds.length)
        });
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Validate the `_data` received as parameter.


## Assessed type

call/delegatecall
Arbitrary coinbase address can be supplied by malicious block proposer to use up another proposer's allowance
Audit: 2024-03-taiko-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-taiko/blob/f58384f44dbf4c6535264a472322322705133b11/packages/protocol/contracts/L1/hooks/AssignmentHook.sol#L115


# Vulnerability details

## Impact
During block proposal, a block proposer is required to pay the prover their prover fee in Ether or an ERC20 fee token. 

The issue with the current code is that a malicious block proposer could use another proposer's pre-approved allowance to the AssignmentHook.sol contract. This would allow the proposer to propose blocks with nothing at stake while the other block proposer falls victim to the stealing.

The issue has been marked as Medium-severity since although it is the proposer's responsibility to make atomic transactions (i.e. approve + propose), it is the protocol's responsibility to protect its users and attacks among block proposer's themselves in order to prevent a certain group of proposer's who do not delve deeper into learning the code. To better put it, we could think of [proposeBlock()](https://github.com/code-423n4/2024-03-taiko/blob/f58384f44dbf4c6535264a472322322705133b11/packages/protocol/contracts/L1/TaikoL1.sol#L55) on TaikoL1 as a blackbox for the proposers.

## Proof of Concept

During block proposal, the functions are called as follows: [proposeBlock()](https://github.com/code-423n4/2024-03-taiko/blob/f58384f44dbf4c6535264a472322322705133b11/packages/protocol/contracts/L1/TaikoL1.sol#L55) on TaikoL1 => [proposeBlock](https://github.com/code-423n4/2024-03-taiko/blob/f58384f44dbf4c6535264a472322322705133b11/packages/protocol/contracts/L1/libs/LibProposing.sol#L68) on LibProposing, which calls => the [onBlockProposed](https://github.com/code-423n4/2024-03-taiko/blob/f58384f44dbf4c6535264a472322322705133b11/packages/protocol/contracts/L1/hooks/AssignmentHook.sol#L61) function while looping through the hook.

The issue currently exists in the onBlockProposed function in the Assignment Hook contract. When the proposer pays the fee to the assigned prover, it requires the proposer to approve certain amount of tokens to the contract in order to allow it to transfer (see [here](https://github.com/code-423n4/2024-03-taiko/blob/f58384f44dbf4c6535264a472322322705133b11/packages/protocol/contracts/L1/hooks/AssignmentHook.sol#L114)).

A block proposer could frontrun the other block proposer's call once the approval is made and supply the coinbase address as the other block proposer's address. This allows the malicious block proposer to use up the tokens that were approved by the other proposer beforehand.


```solidity
File: AssignmentHook.sol
121:         // Find the prover fee using the minimal tier
122:         uint256 proverFee = _getProverFee(assignment.tierFees, _meta.minTier); 
123: 
124:         // The proposer irrevocably pays a fee to the assigned prover, either in
125:         // Ether or ERC20 tokens.
126:         if (assignment.feeToken == address(0)) {
127:             // Paying Ether
128:             _blk.assignedProver.se
send eth can fail and cause DOS
Audit: 2024-03-taiko-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-taiko/blob/f58384f44dbf4c6535264a472322322705133b11/packages/protocol/contracts/libs/LibAddress.sol#L27


# Vulnerability details

## Impact
 the `_to` address is not controlled by the `dev` and if it fails it will
create a Denial of service attack on the `sendEther` function 
## Proof of Concept
if the `_to` address is a contract that has a fallback that reverts ether transfers the transfer will fail 
```solidity
function sendEther(address _to, uint256 _amount, uint256 _gasLimit) internal {
        // Check for zero-address transactions
        if (_to == address(0)) revert ETH_TRANSFER_FAILED();

        // Attempt to send Ether to the recipient address
        (bool success,) = ExcessivelySafeCall.excessivelySafeCall(
            _to,
            _gasLimit,
            _amount,
            64, // return max 64 bytes
            ""
        );

        // Ensure the transfer was successful
        if (!success) revert ETH_TRANSFER_FAILED();
    }
```
## Tools Used

## Recommended Mitigation Steps
use `OpenZeppelin`  contract that checks if an EOA is a contract or not


## Assessed type

DoS
cross function Reentrancy
Audit: 2024-03-taiko-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-taiko/blob/f58384f44dbf4c6535264a472322322705133b11/packages/protocol/contracts/tokenvault/ERC20Vault.sol#L184
https://github.com/code-423n4/2024-03-taiko/blob/f58384f44dbf4c6535264a472322322705133b11/packages/protocol/contracts/tokenvault/ERC20Vault.sol#L148


# Vulnerability details

## Impact
A malicious user can bypass the `changeBridgedToken` mechanism and change token 
any time Bridge and steal tokens 

 ## Proof of Concept

 ```solidity
     function changeBridgedToken(
        CanonicalERC20 calldata _ctoken,
        address _btokenNew
    )
        external
        nonReentrant
        whenNotPaused
        onlyOwner
        returns (address btokenOld_)
    {
        if (_btokenNew == address(0) || bridgedToCanonical[_btokenNew].addr != address(0)) {
            revert VAULT_INVALID_NEW_BTOKEN();
        }

        if (btokenBlacklist[_btokenNew]) revert VAULT_BTOKEN_BLACKLISTED();

        if (IBridgedERC20(_btokenNew).owner() != owner()) {
            revert VAULT_NOT_SAME_OWNER();
        }

        btokenOld_ = canonicalToBridged[_ctoken.chainId][_ctoken.addr];

        if (btokenOld_ != address(0)) {
            CanonicalERC20 memory ctoken = bridgedToCanonical[btokenOld_];

            // The ctoken must match the saved one.
            if (
                ctoken.decimals != _ctoken.decimals
                    || keccak256(bytes(ctoken.symbol)) != keccak256(bytes(_ctoken.symbol))
                    || keccak256(bytes(ctoken.name)) != keccak256(bytes(_ctoken.name))
            ) revert VAULT_CTOKEN_MISMATCH();

            delete bridgedToCanonical[_btokenNew];
            btokenBlacklist[btokenOld_] = true;

            // Start the migration
            IBridgedERC20(btokenOld_).changeMigrationStatus(_btokenNew, false);
            IBridgedERC20(_btokenNew).changeMigrationStatus(btokenOld_, true);
        }

@>        bridgedToCanonical[_btokenNew] = _ctoken;
@>        canonicalToBridged[_ctoken.chainId][_ctoken.addr] = _btokenNew;

        emit BridgedTokenChanged({
            srcChainId: _ctoken.chainId,
            ctoken: _ctoken.addr,
            btokenOld: btokenOld_,
            btokenNew: _btokenNew,
            ctokenSymbol: _ctoken.symbol,
            ctokenName: _ctoken.name,
            ctokenDecimal: _ctoken.decimals
        });
    }
```
## Tools Used

Manual analysis, hardhat.

## Recommended Mitigation Steps
Conforming with Checks Effects Interactions pattern.


## Assessed type

Reentrancy
Tokens with less than 18 decimals allow for incorrect calculations for withdraw
Audit: 2024-03-taiko-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-taiko/blob/f58384f44dbf4c6535264a472322322705133b11/packages/protocol/contracts/team/TimelockTokenPool.sol#L197


# Vulnerability details

## Impact

This can make some users withdraw less funds and some withdraw more funds
## Proof of Concept

```solidity

 function _withdraw(address _recipient, address _to) private {
        Recipient storage r = recipients[_recipient];

        (,,, uint128 amountToWithdraw, uint128 costToWithdraw) = getMyGrantSummary(_recipient);

        r.amountWithdrawn += amountToWithdraw;
        r.costPaid += costToWithdraw;

        totalAmountWithdrawn += amountToWithdraw;
        totalCostPaid += costToWithdraw;

        IERC20(taikoToken).transferFrom(sharedVault, _to, amountToWithdraw);
        IERC20(costToken).safeTransferFrom(_recipient, sharedVault, costToWithdraw);

        emit Withdrawn(_recipient, _to, amountToWithdraw, costToWithdraw);
    }
 function getMyGrantSummary(address _recipient)
        public
        view
        returns (
            uint128 amountOwned,
            uint128 amountUnlocked,
            uint128 amountWithdrawn,
            uint128 amountToWithdraw,
            uint128 costToWithdraw
        )
    {
        Recipient storage r = recipients[_recipient];

        amountOwned = _getAmountOwned(r.grant);
        amountUnlocked = _getAmountUnlocked(r.grant);

        amountWithdrawn = r.amountWithdrawn;
        amountToWithdraw = amountUnlocked - amountWithdrawn;

        // Note: precision is maintained at the token level rather than the wei level, otherwise,
        // `costPaid` must be a uint256.
@>        uint128 _amountUnlocked = amountUnlocked / 1e18; // divide first
        costToWithdraw = _amountUnlocked * r.grant.costPerToken - r.costPaid;
    }
```
This line of code does not take  into consideration tokens that don;t use less
or more than 18 decimals loke
 
e.g tokens like WBTC  have(8 decimals), and  USDC has (6 decimals) 

The `getMyGrantSummary` is used in the `_withdraw` function 
## Tools Used
manual

## Recommended Mitigation Steps
add a whitelist of tokens to check the decimals before you divide `amountUnlocked`


## Assessed type

Decimal