xanoutas/solidity-security-findings huggingface dataset:
The txid in ChakraSettlementUpgradeTest and ChakraSettlementHandler cannot correspond, and cross-chain cannot work properly.
Audit: 2024-08-chakra-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-08-chakra/blob/abef77d95866f2fec93270491fc5abc9ab14f86d/solidity/handler/contracts/ChakraSettlementHandler.sol#L139-L150
https://github.com/code-423n4/2024-08-chakra/blob/abef77d95866f2fec93270491fc5abc9ab14f86d/solidity/settlement/contracts/ChakraSettlementUpgradeTest.sol#L98-L111


# Vulnerability details

## Impact
The txid in ChakraSettlementUpgradeTest and ChakraSettlementHandler cannot correspond, resulting in a revert when the callback is executed in the handler. All cross-chain transactions cannot be completed normally.

## Proof of Concept
background:
1. The txid in [ChakraSettlementUpgradeTest](https://github.com/code-423n4/2024-08-chakra/blob/abef77d95866f2fec93270491fc5abc9ab14f86d/solidity/settlement/contracts/ChakraSettlementUpgradeTest.sol#L98-L111) and [ChakraSettlementHandler](https://github.com/code-423n4/2024-08-chakra/blob/abef77d95866f2fec93270491fc5abc9ab14f86d/solidity/handler/contracts/ChakraSettlementHandler.sol#L139-L150) cannot correspond.
2. In the cross-chain protocol from chain A to chain B, if the cross-chain is successfully executed on chain B, [the callback function of chain A will also be called to complete the cross-chain](https://github.com/code-423n4/2024-08-chakra/blob/abef77d95866f2fec93270491fc5abc9ab14f86d/solidity/settlement/contracts/ChakraSettlementUpgradeTest.sol#L212).
3. [Since the cross-chain message ID is calculated based on the ChakraSettlementUpgradeTest contract](https://github.com/code-423n4/2024-08-chakra/blob/abef77d95866f2fec93270491fc5abc9ab14f86d/solidity/settlement/contracts/ChakraSettlementUpgradeTest.sol#L125). So when this id is passed to the handler, the [corresponding transaction cannot be found](https://github.com/code-423n4/2024-08-chakra/blob/abef77d95866f2fec93270491fc5abc9ab14f86d/solidity/handler/contracts/ChakraSettlementHandler.sol#L378-L381). The handler's callback will revert.

In summary, due to inconsistent txid calculations, the ChakraSettlementUpgradeTest contract cannot be completed cross-chain. Seriously affects the functionality of the protocol.

## Tools Used
manual

## Recommended Mitigation Steps
It is recommended that when updating the Settlement, the calculation method of txid in the handler should be updated simultaneously.


## Assessed type

Other
`MANAGER_ROLE` is inconsistently granted/revoked within the protocol
Audit: 2024-08-chakra-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-08-chakra/blob/abef77d95866f2fec93270491fc5abc9ab14f86d/cairo/handler/src/ckr_btc.cairo#L134-L161


# Vulnerability details

## Impact
To grant and revoke roles, you need help from the `role’s admin`. However, within the protocol, a `manager` can grant/revoke the role from any other `manager`. 

The `owner` and `managers` are not of the same rank. i.e `owner` is the admin of `MANAGER_ROLE`. Therefore, if the `owner` adds/removes a `manager` while another `manager` performs the opposite of the owner's actions, this inconsistency in role management poses governance risk.

## Proof of Concept
In [`SettlementSignatureVerifier`](https://github.com/code-423n4/2024-08-chakra/blob/abef77d95866f2fec93270491fc5abc9ab14f86d/solidity/handler/contracts/SettlementSignatureVerifier.sol#L62-L70) only the `owner` can grant or revoke `MANAGER` role:
```solidity
    function add_manager(address _manager) external onlyOwner {
        grantRole(MANAGER_ROLE, _manager);
        emit ManagerAdded(msg.sender, _manager);
    }

    function remove_manager(address _manager) external onlyOwner {
        revokeRole(MANAGER_ROLE, _manager);
        emit ManagerRemoved(msg.sender, _manager);
    }
```

However, in [`ckr_btc`](https://github.com/code-423n4/2024-08-chakra/blob/abef77d95866f2fec93270491fc5abc9ab14f86d/cairo/handler/src/ckr_btc.cairo#L134-L161) any `MANAGER` can add or remove another `MANAGER`:
```solidity
        fn add_manager(ref self: ContractState, new_manager: ContractAddress) -> bool {
>>          let caller = get_caller_address();
>>          assert(self.chakra_managers.read(caller) == 1, Errors::NOT_MANAGER);
            assert(self.chakra_managers.read(new_manager) == 0, Errors::ALREADY_MANAGER);

            self.chakra_managers.write(new_manager, 1);
            self
                .emit(
                    ManagerAdded {
                        operator: caller, new_manager: new_manager, added_at: get_block_timestamp()
                    }
                );
            return self.chakra_managers.read(new_manager) == 1;
        }
        fn remove_manager(ref self: ContractState, old_manager: ContractAddress) -> bool {
>>          let caller = get_caller_address();
>>          assert(self.chakra_managers.read(caller) == 1, Errors::NOT_MANAGER);
        
            self.chakra_managers.write(old_manager, 0);
            self
                .emit(
                    ManagerRemoved {
                        operator: caller,
                        old_manager: old_manager,
                        removed_at: get_block_timestamp()
                    }
                );
            return self.chakra_managers.read(old_manager) == 0;
        }
```
This allows a `MANAGER` to add or remove another `MANAGER` even though they are not the admin for that role. It is true that the `owner` also has this role:
```solidity
    self.chakra_managers.write(owner, 1); // @audit Granted in the constructor
```
Missing `ERC20Method` validation at destination allows non-transfer tx to be handled as transfers.
Audit: 2024-08-chakra-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-08-chakra/blob/abef77d95866f2fec93270491fc5abc9ab14f86d/solidity/handler/contracts/ChakraSettlementHandler.sol#L318-L330


# Vulnerability details

## Impact
In cross-chain transactions, the integrity and correctness of the transaction must be maintained across different blockchain environments, which might have varying security measures, protocols. However, the missing `ERC20method` validation at destination allows cross-chain tx with a different `ERC20Method` from `Transfer` to be handled as transfer transactions when they actually aren't.

## Proof of Concept
When sending a cross-chain tx, the `sender` locks/burns some amount of tokens at `source` chain. With this, the `recipient` then unlocks or gets minted same amount of tokens at the `destination`.
Basically this is similar to `Token Transfer` from one address to another.

As such, [`handler_erc20::receive_cross_chain_msg()`](https://github.com/code-423n4/2024-08-chakra/blob/abef77d95866f2fec93270491fc5abc9ab14f86d/cairo/handler/src/handler_erc20.cairo#L122) checks to ensure that `ERC20Method` is `TRANSFER` at destination before handling tokens:
```solidity
    assert(transfer.method_id == ERC20Method::TRANSFER, 'ERC20Method must TRANSFER');
```
Checking for the `ERC20Method` at the destination is crucial due to the nature of cross-chain transactions and the need for validation and security on both ends of the transaction.

However [`ChakraSettlementHandler::receive_cross_chain_msg()`](https://github.com/code-423n4/2024-08-chakra/blob/abef77d95866f2fec93270491fc5abc9ab14f86d/solidity/handler/contracts/ChakraSettlementHandler.sol#L318-L330) only decodes the transfer payload and proceeds to handle tokens without actually verifying that the method is transfer.
```solidity
        if (payload_type == PayloadType.ERC20) {
            // Cross chain transfer
            {
                // Decode transfer payload
                ERC20TransferPayload memory transfer_payload = codec
                    .deocde_transfer(msg_payload);
>>              // @audit-info Missing method validation
                if (mode == SettlementMode.MintBurn) {
                    _erc20_mint(
                        AddressCast.to_address(transfer_payload.to),
                        transfer_payload.amount
                    );
                    return true;
                }
                ---SNIP---
            }
            ---SNIP---
        }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
`ERC20Method` should be validated before handling tokens in `receive_cross_chain_msg()`:
```diff
        if (payload_type == PayloadType.ERC20) {
            // Cross chain transfer
            {
                // Decode transfer payload
                ERC20TransferPayload memory transfer_payload = codec
                    .deocde_transfer(msg_payload);
+               require(transfer_payload.method_id == ERC20Method.Transfer, "ERC20Method must TR
ChakraSettlementHandler - to_handler is not checked if it's whitelisted
Audit: 2024-08-chakra-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-08-chakra/blob/main/solidity/handler/contracts/ChakraSettlementHandler.sol#L111


# Vulnerability details

## Impact
When initiating a cross-chain ERC20 settlement via the `ChakraSettlementHandler::cross_chain_erc20_settlement()` function, the `to_handler` is not checked if it's whitelisted anywhere in the execution flow.

This leads to the possibility of users sending cross-chain messages to malicious handlers or handlers that were removed from the whitelist.

It also breaks a key invariant of the protocol stated in the ReadMe:
- The handler receiving a cross-chain message must be on the whitelist for the source chain.

## Proof of Concept
`to_handler` is not checked if it's whitelisted anywhere in the execution flow.

You can follow the execution flow:
`ChakraSettlementHandler::cross_chain_erc20_settlement()` -> `ChakraSettlement::send_cross_chain_msg()`

-> `ChakraSettlement::receive_cross_chain_msg()` -> `ChakraSettlementHandler::receive_cross_chain_msg()`

The only check is in `ChakraSettlementHandler::receive_cross_chain_msg`:
```
        if (is_valid_handler(from_chain, from_handler) == false) {
            return false;
        }
```
But this only checks if the source handler is whitelisted on the source chain. The `to_handler` i.e the handler receiving the cross-chain message is not checked anywhere.

And as stated in the invariant:
- The handler receiving a cross-chain message must be on the whitelist for the source chain

## Tools Used
Manual review
## Recommended Mitigation Steps
Add a check somewhere in the flow that the handler receiving a cross-chain message should be on the whitelist for the source chain.


## Assessed type

Invalid Validation
Users can create 0 amount token transfers, breaking a key invariant of the protocol
Audit: 2024-08-chakra-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-08-chakra/blob/abef77d95866f2fec93270491fc5abc9ab14f86d/cairo/handler/src/handler_erc20.cairo#L167


# Vulnerability details

## Impact
Users can initiate a 0 amount token transfers in `handler_erc20.cairo::cross_chain_erc20_settlement`

This breaks a main invariant of the protocol stated in the contest description:
- Cross-chain ERC20 settlements can only be initiated with a valid amount (greater than 0), a valid recipient address, a valid handler address, and a valid token address.

It also opens up the possiblity of creating a large amount of cross-chain settlements potentially overwhelming the system with no cost because of 0 amount tokens transfers.
## Proof of Concept
There is no such checks in the cairo `receive_cross_chain_msg` function. 

This breaks a main invariant of the protocol that is correctly enforced in the solidity version:
```
        require(amount > 0, "Amount must be greater than 0");
        require(to != 0, "Invalid to address");
        require(to_handler != 0, "Invalid to handler address");
        require(to_token != 0, "Invalid to token address");
```
which makes it even more apparent that these checks should be enforced.
## Tools Used
Manual review
## Recommended Mitigation Steps
Implement the checks in the cairo handler just like in the solidity one.


## Assessed type

Invalid Validation
No access control in 'send_cross_chain_msg' can lead to DoS attacks.
Audit: 2024-08-chakra-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-08-chakra/blob/main/solidity/settlement/contracts/ChakraSettlement.sol#L111-L133


# Vulnerability details

Note: In this submission, the vulnerability is referred to the solidity codebase, but this can apply also for the Cairo codebase.

## Vulnerability details 
In `'ChakraSettlement:send_cross_chain_msg'` there is not access control, and this can lead to DoS attack by malicious users that could call repeatedly this function in order to create a `'CrossChainMsg'` event that will be constantly supervised by validators. 

This is amplified by the fact that the gas value to use in order to call `'ChakraSettlement:send_cross_chain_msg'` is pretty low.

## Impact
A DoS attack can influence the activity of the validators, that may not process the  legit transactions in certain moments, leaving them in a pending status very long. 

## Proof of Concept
Here there is a test (done in Foundry) when a malicious users calls `'ChakraSettlement:send_cross_chain_msg'` multiples times (1000 times).  
```solidity 
function test_sendCrossChainTx_Directly_ManyTimes() public {
vm.selectFork(arbFork);
address marco = address(123); 
vm.startPrank(marco); 
for(uint256 i = 0; i < 1000; i++){
chakraSettlementArb.send_cross_chain_msg("Ethereum", marco, uint160(address(chakraSettlementHandlerEth)), PayloadType.ERC20, "");
}
}
```

## Tools Used
Manual review, Foundry

## Recommended Mitigation Steps
Since `'ChakraSettlement:send_cross_chain_msg'` is supposed to be called only by the handler contract, implement an access control that requires that the sender is a valid handler contract. 

Consider adding this modifier in `'ChakraSettlement'`: 
```solidity
modifier IsValidHandler(address handler) {
require(isValidHandler[handler] == true, "Not a valid handler"); 
_;
}
```

And then implement this modifier in `'ChakraSettlement:send_cross_chain_msg'`: 

```solidity
function send_cross_chain_msg(
        string memory to_chain,
        address from_address,
        uint256 to_handler,
        PayloadType payload_type,
        bytes calldata payload
    ) external IsValidHandler(msg.sender){
        nonce_manager[from_address] += 1; 

        address from_handler = msg.sender;

        uint256 txid = uint256(
            keccak256(
                abi.encodePacked(
                    contract_chain_name, // from chain
                    to_chain,
                    from_address, // msg.sender address
                    from_handler, // settlement handler address
                    to_handler,
                    nonce_manager[from_address]
                )
            )
        );

        create_cross_txs[txid] = CreatedCrossChainTx(
            txid,
            contract_chain_name,
            to_chain,
            from_address,
            from_handler,
            to_handler,
            payload,
            CrossChainMsgStatus.Pending
        );

        emit CrossChainMsg(
            txid,
            from_addre
Improper txid generation break the invariants
Audit: 2024-08-chakra-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-08-chakra/blob/d0d45ae1d26ca1b87034e67180fac07ce9642fd9/cairo/handler/src/settlement.cairo#L284-L314


# Vulnerability details

## Impact
In Cairo implementation, the txid's generated with get_tx_info and msg_count. This breaks one invariant: `Cross-chain transaction IDs are uniquely generated based on the source and destination chain information, sender, handler addresses, and nonce.`

## Proof of Concept
In cairo handler implementation, the txid will be generated based on get_tx_info() and msg_count. But from readme, we can find one invariants:
`Cross-chain transaction IDs are uniquely generated based on the source and destination chain information, sender, handler addresses, and nonce.`.
The txid generation in cairo handler implementation will break the invariant.

```javascript
        fn cross_chain_erc20_settlement(ref self: ContractState, to_chain: felt252, to_handler: u256, to_token: u256, to: u256, amount: u256) -> felt252{
            ...
            let tx_id = LegacyHash::hash(get_tx_info().unbox().transaction_hash, self.msg_count.read());
```

## Tools Used
Manual

## Recommended Mitigation Steps
Change the txid generation method to match the invariants.


## Assessed type

Error
`PoolV3:: repayCreditAccount` is invalidly handling loss when `CDPVault:: liquidatePositionBadDebt` is called
Audit: 2024-10-loopfi-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-10-loopfi/blob/main/src/CDPVault.sol#L702
https://github.com/code-423n4/2024-10-loopfi/blob/main/src/PoolV3.sol#L593


# Vulnerability details

## Description

https://github.com/code-423n4/2024-07-loopfi-findings/issues/57 showed a scenario where the profit from the interest is not being registered correctly. It shows a mitigation that consists of 2 parts, but only 1 was implemented which reversed the issue and made it more severe.

The mitigation that was added was `pool.repayCreditAccount(debtData.debt, profit, loss);`, `profit` was set to 0 before the mitigation. 

## Proof of Concept
From the original report:
>First we need to understand how liquidatePositionBadDebt() works. When there is a bad debt detected for a position, any liquidator can come and liquidate the position. The liquidator is required to buy ALL the collateral at a discount price, and the loss (totalDebt - repayAmount) is sent to PoolV3 to be beared by the lpETH stakers.
>
>This is all good, but the issue here is that when the liquidator repays the debt, it is compared against the totalDebt of the position, which includes the interest. This interest should also be sent to the lpETH stakers, but is currently not, which would result in loss of funds for the lpETH stakers.
>
>An example:
>1. CDPVault position has debt principal == 1000, debt interest == 500. Total debt == 1500. Collateral value == 1600, discount == 90%, discount value == 1440.
>2. Liquidator comes and pay off 1440, loss is 1500-1440 = 60, so pool.repayCreditAccount(1000, 0, 60) is called.
>3. The loss is 60, and the same amount of lpETH is burned from the StakingLPEth contract.

Here comes the reversed impact, where with the current implementation only the profit will be registered because of `PoolV3::repayCreditAccount:
```solidity
if (profit > 0) {
    _mint(treasury, _convertToShares(profit)); // U:[LP-14B]
} else if (loss > 0) {
    ...
    _burn(treasury_, sharesToBurn); // U:[LP-14C,14D]
}
```
Where only the profit part will run, and the "else if" part is thrown away. This means that the loss amount that happened (60) won't be registered and the treasury shares won't be burned, messing up the assets/shares ratio, which affects all `lpETH` stakers forcing their shares to be worth fewer assets, i.e. losing funds.


## Recommended Mitigation Steps

In `PoolV3::repayCreditAccount`:
```diff
        if (profit > 0) {
            _mint(treasury, convertToShares(profit)); // U:[LP-14B]
+       }
+       if (loss > 0)
-       } else if (loss > 0) {
            ...
        }
```






## Assessed type

Invalid Validation
Malicious users can cause reward loss for position owners
Audit: 2024-10-loopfi-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-10-loopfi/blob/main/src/pendle-rewards/RewardManagerAbstract.sol#L41-L68


# Vulnerability details


## Impact
The current implementation allows anyone to call `getRewards` for any position owner, potentially leading to:
- Position owners may lose out on rewards if someone else calls `getRewards` during a period of low or zero reward accumulation.

This can result in an unfair distribution of rewards, potential loss of rewards for unsuspecting users, and overall undermining of the reward system's integrity.

## Proof of Concept
The issue is rooted in the implementation of the `_distributeRewardsPrivate` function in the [RewardManagerAbstract](https://github.com/code-423n4/2024-10-loopfi/blob/main/src/pendle-rewards/RewardManagerAbstract.sol#L52) contract:

```solidity
function _distributeRewardsPrivate(
    address user,
    uint256 collateralAmountBefore,
    address[] memory tokens,
    uint256[] memory indexes
) private {
    assert(user != address(0) && user != address(this));

    uint256 userShares = collateralAmountBefore;
    for (uint256 i = 0; i < tokens.length; ++i) {
        address token = tokens[i];
        uint256 index = indexes[i];
        uint256 userIndex = userReward[token][user].index;

        if (userIndex == 0) {
            userIndex = INITIAL_REWARD_INDEX.Uint128();
        }

        if (userIndex == index) continue;

        uint256 deltaIndex = index - userIndex;

        uint256 rewardDelta = userShares.mulDown(deltaIndex);
        uint256 rewardAccrued = userReward[token][user].accrued + rewardDelta;
        userReward[token][user] = UserReward({index: index.Uint128(), accrued: rewardAccrued.Uint128()});
    }
}
```

The `getRewards` function in [RewardManager](https://github.com/code-423n4/2024-10-loopfi/blob/main/src/pendle-rewards/RewardManager.sol#L59-L63) can be called by anyone for any user:

```solidity
function handleRewardsOnWithdraw(
    address user,
    uint collateralAmountBefore,
    int256 deltaCollateral
) external virtual onlyVault returns (address[] memory tokens, uint256[] memory amounts, address to) {
    _updateAndDistributeRewards(user, collateralAmountBefore, deltaCollateral);
    return _doTransferOutRewards(user);
}
```

The flow of the issue is as follows:
. An attacker monitors the reward accumulation rate.
. When the reward rate is low or zero (resulting in `rewardDelta` being zero), the attacker calls `getRewards` for a target user.
. This updates the user's `userIndex` without accruing any rewards.
. When the reward rate increases later, the user will miss out on the rewards for the period between the attacker's call and the increased rate.

Additionally, users who understand this mechanism can call `getRewards` frequently during high reward periods to maximize their returns, creating an unfair advantage.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Implement access control on the `getRewards` function to ensure only t