xanoutas/solidity-security-findings huggingface dataset:
Absence of slippage protection || mechanism in the Absorber.provide() would lead to MEV Based Attacks.
Audit: 2024-01-opus-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-01-opus/blob/4720e9481a4fb20f4ab4140f9cc391a23ede3817/src/core/absorber.cairo#L397


# Vulnerability details

## Impact
The bug originates from the absence of slippage control in the Absorber.provide() method. This method facilitates the deposit of yin tokens, allowing users to receive shares in return. However, the exchange rate from yin to shares heavily relies on the balance_of(absorber) function, which is susceptible to manipulation through a donation attack or front-running by malicious users. This manipulation directly impacts the exchange rate of shares to yin, potentially leading to significant discrepancies between expected and received amounts. Consequently, users may be forced to accept undesirable exchange rates when their transactions are eventually mined.

## Proof of Concept
https://github.com/code-423n4/2024-01-opus/blob/4720e9481a4fb20f4ab4140f9cc391a23ede3817/src/core/absorber.cairo#L681


## Tools Used:Snforge

## Recommended Mitigation Steps:Simply add a min_shares parameter(used to represent the user min acceptable shares ) and after computing shares add an assertion to check that computed_shares>=min_shares.
i.e(assert(min_shares<=computed_shares)





## Assessed type

MEV
Users can be Unfairly Punished for Failures External to them, such as Delays in Transaction Processing or Sequencer Crashes.
Audit: 2024-01-opus-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-01-opus/blob/4720e9481a4fb20f4ab4140f9cc391a23ede3817/src/core/absorber.cairo#L441-L460


# Vulnerability details

## Impact
Users who create a request to withdraw their yin from the absorber can be unfairly penalized in cases where the sequencer crashes or delays the transaction processing (something that has already happened several times in the network) this can cause the users can't withdraw yin if the absorber uses this yin to liquidate troves while the users are unable to remove the yin because they are waiting more time than they should due to being penalized for the sequencer problems. 

If users depend on this yin to repay a trove, they can be at risk of being liquidated if they are unable to withdraw the yin when they need it to repay the trove.

## Proof of Concept
In the ***Absorber*** contract any user can deposit (Provide function) and withdraw (Remove function) Yin to earn rewards and a portion of the collateral absorbed tokens from the shrine, the users are allowed to deposit and withdraw Yin at any time they want, but they can be unfairly penalized for the sequencer problems and this can cause that users can't withdraw their yin when they need, because while the users are waiting for the unfair penalized time to elapse the absorber can use the ying they want to withdraw, thus leaving users without the possibility of withdrawing the yin they need.

Let's look at the scenario mentioned before:

1. A bunch of users provide yin to start earning rewards and collateral in the ***absorber contract***.
https://github.com/code-423n4/2024-01-opus/blob/4720e9481a4fb20f4ab4140f9cc391a23ede3817/src/core/absorber.cairo#L397-L429
```cairo
// Some parts of the code have been omitted for simplicity.

        fn provide(ref self: ContractState, amount: Wad) {
            // total shares is below the minimum initial shares.
            let (new_provision_shares, issued_shares) = self.convert_to_shares(amount, false);

            // If epoch has changed, convert shares in previous epoch to new epoch's shares
            let current_epoch: u32 = self.current_epoch.read();
            let converted_shares: Wad = self.convert_epoch_shares(provision.epoch, current_epoch, provision.shares);

            let new_shares: Wad = converted_shares + new_provision_shares;
            self.provisions.write(provider, Provision { epoch: current_epoch, shares: new_shares });

            // Update total shares for current epoch
            self.total_shares.write(self.total_shares.read() + issued_shares);

            let success: bool = self.yin_erc20().transfer_from(provider, absorber, amount.into());
            assert(success, 'ABS: Transfer failed');
}
```
2. Now, some time passed the users earned some rewards, so some users want to withdraw all or just some part of their yin from the **Absorber** to use it in any other strategy, so they create a request to withdraw their yin calling the **request** function
ERC777 reentrancy allows attacker to drain reward in Absorber
Audit: 2024-01-opus-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-01-opus/blob/04583e0411dbf8027952d668a8678fda0cb5b160/src/core/absorber.cairo#L793


# Vulnerability details

## Impact
The `reap_helper(...)` function in the Absorber is used to calculate and transfer the absorbed assets and rewards to the provider. It's triggered whenever a provider takes any action.

However, this function doesn't follow the Check-Effects-Interaction (CEI) pattern. As the code snippet below shows, it first calculates the rewards, transfers the rewards to the provider, and then updates the provider's cumulative rewards.

If the token is ERC777 or any token with a call hook to the receiver, an attacker could call back to the Absorber and drain the reward token. This is possible because the cumulative rewards are updated after token transfers.
```rust
fn reap_helper(ref self: ContractState, provider: ContractAddress, provision: Provision) {
    ...
    let (absorbed_assets, rewarded_assets) = self
        .get_absorbed_and_rewarded_assets_for_provider(provider, provision, false);

    // Get and update provider's absorption ID
    self.provider_last_absorption.write(provider, self.absorptions_count.read());

    // Loop over absorbed and rewarded assets and transfer
    self.transfer_assets(provider, absorbed_assets);
  
    // @audit ERC777 reentrancy. Provider's cumulative rewards are updated later
    self.transfer_assets(provider, rewarded_assets); 
  
    ...
    self.update_provider_cumulative_rewards(provider);

    self.emit(Reap { provider, absorbed_assets, reward_assets: rewarded_assets });
}
```

## Proof of Concept
The Absorber has multiple epochs. A new epoch is created when the amount of yin per share drops below a threshold. However, for simplicity, we'll consider only one epoch which is the current epoch in the scenario below:

1. In the current epoch, assume `asset_amt_per_share = 1000` and `provider_last_reward_cumulative = 0` is the attacker's last reward cumulative. The attacker has `epoch_shares = 1000` shares in the current epoch.
2. The attacker calls `reap()`, which in turn calls `reap_helper()`. This function calls `get_provider_rewards()` to calculate the provider reward using the code snippet below. The result is `reward_amt = 1000 * (1000 - 0) = 1e6`.
```rust=
let rate: u128 = if epoch == provision.epoch {
    asset_amt_per_share - self.provider_last_reward_cumulative.read((provider, reward.asset))
} else {
    asset_amt_per_share
};
reward_amt += u128_wmul(rate, epoch_shares.val);
```

3. The `reap_helper()` function calls `transfer_assets()` to transfer `1e6` reward to the attacker. The token has a call hook to the receiver/attacker address, allowing the attacker to take control and call `reap()` again.
4. Since `provider_last_reward_cumulative` is not updated, the same calculation occurs, and the contract continues to transfer `1e6` reward to the attacker. Attacker can repeat these steps until the pool is empty.

## Tools Used
Manual Review

## Rec
Duplicated recipients can override `percentages[recipient]` and lead to incorrect allocation
Audit: 2024-01-opus-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-01-opus/blob/04583e0411dbf8027952d668a8678fda0cb5b160/src/core/allocator.cairo#L152


# Vulnerability details

## Impact 
The function `set_allocation_helper()` does not prevent duplicate addresses in the input parameters `recipients`. If duplicate recipient addresses are provided, the `percentages[]` mapping can only record the last percentage value for the same recipient address. 

```rust
loop {
    match recipients_copy.pop_front() {
        Option::Some(recipient) => {
            self.recipients.write(idx, *recipient);

            let percentage: Ray = *(percentages_copy.pop_front().unwrap());
            // @audit If duplicated recipients
            //        percentages[recipient] will be overriden
            self.percentages.write(*recipient, percentage); 

            total_percentage += percentage;

            idx += 1;
        },
        Option::None => { break; }
    };
};
```
However, function `get_allocation()` does not care about it but just looping through `recipients_count` which is the length of `recipients` list when being added.

## Proof of Concept
Consider the scenario with input parameters
`recipients = [0x1, 0x1, 0x2]`, `percentages = [0.3, 0.2, 0.5]`.

Although recipient `0x1` is allocated twice with the percentages `0.3` and `0.2`, the function `percentages[0x1]` will only store one value: `0.2`.

So when calling `get_allocation()`, it will return
`recipients = [0x1, 0x1, 0x2]`, `percentages = [0.2, 0.2, 0.5]`

## Tools Used
Manual Review

## Recommended Mitigation Steps
Prevent duplicate recipient addresses in the function `set_allocation_helper()`.



## Assessed type

Other
Adjusting a CDP allows a user to reduce their collateral under the threshold enforced by the protocol
Audit: 2024-06-badger-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-badger/blob/main/ebtc-zap-router/src/EbtcLeverageZapRouter.sol#L403-L467


# Vulnerability details

## Impact

Users have the ability to reduce the risk of their position and bypass the protocol design restrictions, effectively reducing the rewards intended for liquidators and getting rid of the 0.2 stETH gas stipend in the position.

## Proof of concept

The protocol enforces users to deposit at least 2 stETH, as stated in [the documentation](https://docs.ebtc.finance/ebtc/protocol-mechanics/borrowing) : "CDPs must have a size of at least 2 stETH of collateral"

A "gas stipend" of 0.2 stETH is required to be transferred "in addition to the collateral" as an incentive for the liquidators "to cover the transaction's gas cost".

This essentially means a user must deposit 2.2 stETH when a CDP is opened. This is enforced in `_openCdp()` through the `_requireAtLeastMinNetStEthBalance()` function.

[https://github.com/code-423n4/2024-06-badger/blob/main/ebtc-zap-router/src/EbtcLeverageZapRouter.sol#L217](https://github.com/code-423n4/2024-06-badger/blob/main/ebtc-zap-router/src/EbtcLeverageZapRouter.sol#L217)

```solidity
// @audit LIQUIDATOR_REWARD == 2e17
_requireAtLeastMinNetStEthBalance(_stEthDepositAmount - LIQUIDATOR_REWARD);

...

function _requireAtLeastMinNetStEthBalance(uint256 _stEthBalance) internal pure {
    // @audit `MIN_NET_STETH_BALANCE` == 2e18
    require(
        _stEthBalance >= MIN_NET_STETH_BALANCE,
        "ZapRouterBase: Cdp's net stEth balance must not fall below minimum"
    );
}
```

However, a user has the ability to adjust his CDP to reduce his collateral under this 2.2 stETH because it does not implement sufficient checks.

Here is a modified version of the `test_adjustCdp_debtDecrease_stEth` test that demonstrates the collateral being under 2.2 stETH after adjustment

```solidity
function test_adjustCdp_under_threshold() public {
    seedActivePool();

    (address user, bytes32 cdpId) = createLeveragedPosition(MarginType.stETH);

    IEbtcZapRouter.PositionManagerPermit memory pmPermit = createPermit(user);
    uint256 debtChange = 0.99e18;
    uint256 marginBalance = 2.87e18;
    uint256 collValue = _debtToCollateral(debtChange) * 10004 / 10000;
    _before();
    vm.startPrank(user);
    leverageZapRouter.adjustCdp(
        cdpId, 
        _getAdjustCdpParams(debtChange, -int256(debtChange), -int256(collValue), -int256(marginBalance), false), 
        abi.encode(pmPermit),
        _getExactInCollateralToDebtTradeData(collValue)
    );
    vm.stopPrank();
    _after();

    // Test zap fee (no fee if debt decrease)
    assertEq(eBTCToken.balanceOf(testFeeReceiver), 1e18 * defaultZapFee / 10000); 

    _checkZapStatusAfterOperation(user);

    pmPermit = createPermit(user);

    vm.startPrank(user);
    (uint256 debt, uint256 collShares) = cdpManager.getSyncedDebtAndCollShares(cdpId);
    
    assertLt(
        collShares,
        2.004e18
    );

    vm.stopPrank(
Price manipulation in the DEX
Audit: 2024-06-badger-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-zap-router/src/EbtcLeverageZapRouter.sol#L403
https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-zap-router/src/EbtcLeverageZapRouter.sol#L397


# Vulnerability details

## Impact
when users want to add collateral by calling the `EbtcLeverageZapRouter::_adjustCdp` function, If users could repeatedly mint debt and swap it to stEth to repay the flash loan of activePool, then this number of swapping decreases the price of ebtc token and increases the price of stEth cause we keep buying it with the flash loan.
simply say, we add collateral with a flash loan to mint more debt to swap more ebtc to stEth to manipulate the price.

Users by calling the `_adjustCdp` function can provide a situation where they could Have the same CR but with different values of Collateral and Debt. for example, if a user has a valid CR of 130% with 17.5 collateral and 1 debt, they can have the same CR but with 175 coll and 10 debt. 
at first glance, this isn't an issue cause the CR is healthy but we did increase the demand for the stEth token in the stEth/eBTC pair with flash loan which led to a decrease in the eBTC token price, overall a price manipulation
with the `_adjustCdp` function, users can set the margin balance to 0 to don't pay anything.
another thing is if the CR of the accrued CDP goes underwater, the user can liquid himself with a flash loan of another protocol and enjoy the accrued collateral

## Proof of Concept
append this test to the `LeverageZaps.t.sol`:
```js
 
 function test_adjustCdp_debtIncrease_collIncrease_withoutMargin_stEth() public {
        seedActivePool();


        // debt = 1e3, marginAmount = 3.5 ether
        (address user, bytes32 cdpId) = createLeveragedPosition(MarginType.stETH);

        IEbtcZapRouter.PositionManagerPermit memory pmPermit = createPermit(user);

        uint256 debtChange = 2.24e18;
        uint256 marginIncrease = 0;
        uint256 collValue = 30e18;
        uint256 flAmount = 30e18;

        _before();
        vm.startPrank(user);
        leverageZapRouter.adjustCdp(
            cdpId,
            _getAdjustCdpParams(flAmount, int256(debtChange), int256(collValue), int256(marginIncrease), false),
            abi.encode(pmPermit),
            _getExactInDebtToCollateralTradeData(debtChange)
        );
        vm.stopPrank();
        _after();

        (, uint256 collShares) = cdpManager.getSyncedDebtAndCollShares(cdpId);

        assertGe(collateral.balanceOf(user), 10000e18 - 3.5e18); 
        // the user now has approximately 33.5 coll but only paid 3.5 stEth and increased the demand for the stEth
        assertGe(collShares, 3.5e18);   
        _checkZapStatusAfterOperation(user);
    }


```
this test uses `Mock1Inch` as DEX and the price is constantly equal to `1 ebtc = 13.46 stEth` and no slippage. you should consider that every ebtc swap for stEth increas
Incorrect collateral calculation
Audit: 2024-06-badger-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-zap-router/src/LeverageZapRouterBase.sol#L142-L144
https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-zap-router/src/LeverageZapRouterBase.sol#L188
https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-zap-router/src/EbtcLeverageZapRouter.sol#L398-L400
https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-zap-router/src/EbtcLeverageZapRouter.sol#L181


# Vulnerability details

## Impact
Collateral is incorrectly calculated, resulting in incorrect parameters for cdp that underestimate the user's total collateral balance. Results in loss of funds for the user as cdps with adequate collateral can be deemed unhealthy, and are later redeemed for a lesser amount of collateral than deposited. 

## Proof of Concept
When depositing margin either by adjusting an existing cdp or opening a new cdp, the steth amount is transferred in and the balance change in steth tokens is used to determine the margin change of the cdp.

However, this margin change is then converted again into shares, and the values are treated like they are eth rather than steth tokens as the `steth.getSharesByPooledEth` function is called on them. As a result, the actual collateral amount recorded is an underestimate since this function is monotonically decreasing as the amount of pooled eth goes up. 

## Tools Used
Manual review.

## Recommended Mitigation Steps
Use the margin change directly when calculating the collateral of cdps, rather than passing this value through `steth.getSharesByPooledEth`


## Assessed type

ERC20
Upgraded Q -> 3 from #48 [1721695957843]
Audit: 2024-06-badger-findings
Severity: MEDIUM

Judge has assessed an item in Issue #48 as 3 risk. The relevant finding follows:

 ## [QA-02] False comparison logic in `_doCheckValueType` leading to incorrect post-operation checks

### Impact
The incorrect comparison logic in the `_doCheckValueType` function can lead to incorrect validation of post-operation checks. This will result in the ptotocol not properly verifying the expected conditions, thereby, allowing operations to proceed when they should not, or failing valid operations. 

### Proof of Concept
The `_doCheckValueType` function is intended to check if a value meets certain conditions based on the operator provided. However, there is a mistake in the comparison logic for the `gte` and `lte` operators.

Look at this part of the code: https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-protocol/packages/contracts/contracts/LeverageMacroBase.sol#L277-L290

```solidity
function _doCheckValueType(CheckValueAndType memory check, uint256 valueToCheck) internal {
    if (check.operator == Operator.skip) {
        // Early return
        return;
    } else if (check.operator == Operator.gte) {
        require(check.value >= valueToCheck, "!LeverageMacroReference: gte post check");
    } else if (check.operator == Operator.lte) {
        require(check.value <= valueToCheck, "!LeverageMacroReference: let post check");
    } else if (check.operator == Operator.equal) {
        require(check.value == valueToCheck, "!LeverageMacroReference: equal post check");
    } else {
        revert("Operator not found");
    }
}
```

The problem here is with the `gte` and `lte` checks. The `gte` (greater than or equal to) check should ensure that `valueToCheck` is greater than or equal to `check.value`, and the `lte` (less than or equal to) check should ensure that `valueToCheck` is less than or equal to `check.value`. However, the current implementation does the opposite.

### Recommended Mitigation Steps
The comparison logic should be corrected to ensure that the `gte` and `lte` checks are performed correctly:

```diff
function _doCheckValueType(CheckValueAndType memory check, uint256 valueToCheck) internal {
    if (check.operator == Operator.skip) {
        // Early return
        return;
    } else if (check.operator == Operator.gte) {
-        require(check.value >= valueToCheck, "!LeverageMacroReference: gte post check");
+        require(valueToCheck >= check.value, "!LeverageMacroReference: gte post check");
    } else if (check.operator == Operator.lte) {
-        require(check.value <= valueToCheck, "!LeverageMacroReference: let post check");
+        require(valueToCheck <= check.value, "!LeverageMacroReference: lte post check");
    } else if (check.operator == Operator.equal) {
        require(valueToCheck == check.value, "!LeverageMacroReference: equal post check");
    } else {
        revert("Operator not found");
    }
}
```
Add Require Statement to Handle Potential Failures in _depositRawEthIntoLido Function
Audit: 2024-06-badger-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-badger/blob/9173558ee1ac8a78a7ae0a39b97b50ff0dd9e0f8/ebtc-zap-router/src/ZapRouterBase.sol#L34-L41


# Vulnerability details

### Description
The _depositRawEthIntoLido function in the ZapRouterBase contract performs a low-level call to the Lido contract without handling potential failures. This could lead to silent failures and unexpected behavior.

### Issue
The `call` to the Lido contract could fail without being detected, leading to incorrect balance calculations.

## Tools Used
Manual review

## Recommended Mitigation Steps
```
function _depositRawEthIntoLido(uint256 _initialETH) internal returns (uint256) {
    uint256 _balBefore = stEth.balanceOf(address(this));
    (bool success, ) = payable(address(stEth)).call{value: _initialETH}("");
    require(success, "EbtcZapRouter: depositRawEthIntoLido failed");

    uint256 _deposit = stEth.balanceOf(address(this)) - _balBefore;
    return _deposit;
}
```


## Assessed type

Error