xanoutas/solidity-security-findings huggingface dataset:
A staked service may not be rewarded as available rewards may show obsolete value
Audit: 2024-05-olas-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-olas/blob/main/registries/contracts/staking/StakingBase.sol#L721


# Vulnerability details

## Impact
The `availableRewards` is not guaranteed to have the latest updated value. So, during staking a service, its value must be updated beforehand. Otherwise, there could be a situation that a staked service does not earn any reward despite multisig transaction execution.

## Proof of Concept
 - Suppose available rewards is 0.01 ETH. 

 - Then, Service1 is staked, and its multisig executes transactions.

 - One liveness period is elapsed.

 - Suppose, the service1 should be rewarded 0.01 ETH (equal to available rewards) because of multisig transaction execution, if the function `checkpoint()` is called. **But, suppose this function is not called yet, so available rewards is still equal to 0.01 ETH.**

 - Then, service2 is staked. It will be staked successfully because the available rewards is still nonzero.
 ```solidity
     function stake(uint256 serviceId) external {
        // Check if there available rewards
        if (availableRewards == 0) {
            revert NoRewardsAvailable();
        }
        //....
     }
 ```
 https://github.com/code-423n4/2024-05-olas/blob/main/registries/contracts/staking/StakingBase.sol#L721

 - Then, before the service2 multisig executes any transactions, the function `checkpoint()` is called by service1 owner (or anyone). By doing so, the rewards allocated to service1 will be calculated (which is equal to 0.01 ETH), and available rewards will be updated to zero.
 https://github.com/code-423n4/2024-05-olas/blob/main/registries/contracts/staking/StakingBase.sol#L661

 - Then, service2 multisig executes transactions. It is expected that because of multisig transaction execution, rewards will be allocated to service2. But, since the `checkpoint()` is called just before and all the available rewards are allocated to service1, nothing is left for service2.

 - Now, the service1 has 0.01 ETH reward that can be claimed any time. Regarding service2, although multisig has executed transactions, there is no reward to be allocated to it.

 The root cause of this issue is that when a service is staked, it only checks that what the current value of `availableRewards` is. If it is nonzero, it assumes that there is available rewards to be allocated to this service if multisig executes transaction, so it allows to stake. But, it is not guaranteed that the `availableRewards` is correct and updated. Because, `availableRewards` can include the rewards allocated to other services that have not claimed yet (or the `checkpoint()` is not called yet), so its value could be obsolete. 

 In other words, when staking a service, the value of `availableRewards` should be updated by calling `checkpoint()`. But since the `checkpoint()` calls the internal function `_calculateStakingRewards`, and this function can update only if `livenessPeriod` is elapsed from the last checkpoint, there shoul
syncWithheldTokens on Arbitrum could fail to be delivered.
Audit: 2024-05-olas-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-olas/blob/3ce502ec8b475885b90668e617f3983cea3ae29f/tokenomics/contracts/staking/DefaultTargetDispenserL2.sol#L323-L345


# Vulnerability details

# **Vulnerability Detail**

The execution flow begins on the `ArbitrumDispenserL2`, in `syncWithheldTokens` function. Any user can call this function to syncs withheld token amount with L1. However, if the user forge the `msg.value along` with the call on L2, so it will be enough to proceed on L2, but not enough to be delivered on L1, the tx could fail.

# **Proof of Concept**

The msg.value that user provide on L2, will be used to execute the call on L1. We can see it in the L2ToL1MessageNitro.ts in Arbitrum. `Callvalue` is `msg.value`

```solidity
return await outbox.executeTransaction(
      proof,
      this.event.position,
      this.event.caller,
      this.event.destination,
      this.event.arbBlockNum,
      this.event.ethBlockNum,
      this.event.timestamp,
      this.event.callvalue,
      this.event.data,
      overrides || {}
    )
  }
```

Consequently , in the Outbox.sol the executeBridgeCall on L1 function will be triggered.

```solidity
function executeBridgeCall(
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        (bool success, bytes memory returndata) = bridge.executeCall(to, value, data);
```

In the bridge it will trigger this logic.

```solidity
(success, returnData) = to.call{value: value}(data);
```

So we can see that everything is based on msg.value and if the tx is failed to be executed (due to out of gas error) it will not be possible to re-execute this function. 

# **Impact**
The tx could fail on L1, but the `withheldAmount` would be set to 0 (result of successful execution) on L2. Consequently, the DAO will be force to manually restore the data.

# **Recommendation**

Ensure that enough msg.value is passed on L2. Or, ensure that only whitelisted user’s could call this function.


## Assessed type

Other
Node owners can circumvent slashing penalties.
Audit: 2024-09-karak-mitigation-findings
Severity: HIGH

# Lines of code

https://github.com/karak-network/karak-restaking/tree/v2/src/entities/NativeVault.sol#L509-L517


# Vulnerability details

## Impact

Node owners can circumvent slashing penalties.

## Proof of Concept

When decreasing balance of a node owner, he may have fewer shares than what should be burned. In this case, all of his shares are burned. However, `totalRestakedETH` is still reduced by the original amount of assets, which can be exploited by malicious users to circumvent slashing.

```solidity
    function _decreaseBalance(address _of, uint256 assets) internal {
        NativeVaultLib.Storage storage self = _state();
511:    uint256 shares = Math.min(convertToShares(assets), balanceOf(_of));
512:    _beforeWithdraw(assets, shares);
        _burn(_of, shares);
        self.totalAssets -= assets;
515:    self.ownerToNode[_of].totalRestakedETH -= assets;
        emit DecreasedBalance(self.ownerToNode[_of].totalRestakedETH);
    }
```

This vulnerability can be exploited by malicious users to circumvent slashing. 
While the Merkle tree can reflect the change in the validator's balance immediately upon inclusion of the withdrawal transaction, the actual crediting of unstaked ethers to the withdrawal address may take additional time depending on the transaction's finality and processing.

Consider the following scenario:
1. Both Alice and Bob each add a validator with 32 ETH and successfully complete a snapshot.

    Alice's totalRestakedETH : 32 ETHER
    Bob's totalRestakedETH : 32 ETHER
    totalAssets : 64

2. Slashing occurs, causing `totalAssets` to decrease to 60 ETHER.

    totalAssets : 60
    Alice's totalRestakedETH : 32 ETHER
    convertToAssets(balanceOf(Alice)) : 30 ETHER
    Bob's totalRestakedETH : 32 ETHER
    convertToAssets(balanceOf(Bob)) : 30 ETHER

3. Alice requests a full withdrawal from her validator.
4. The Merkle tree is updated; however, the unstaked ethers are not yet credited to the withdrawal address.
5. Alice initiates a snapshot, completing it before the unstaked ethers are credited.
 (On Ethereum, it can typically take anywhere from a few seconds to several minutes. However, in some cases, it might take longer.)
    Her balance decreases by 32 ETHER. Since Alice does not have enough to burn, all her shares are burned (L511-L512).
    
    totalAssets : 60 - 32 = 28
    Alice's totalRestakedETH: 32 ETHER - 32 ETHER = 0 (L515).
    Bob's totalRestakedETH : 32 ETHER
    convertToAssets(balanceOf(Bob)) : 28 ETHER (Bob has all remaining shares.)

6. Once the unstaked ethers are credited to the withdrawal address, Alice starts another snapshot.
    Her withdrawable balance is now 32 ETHER.
    
    totalAssets : 60
    Alice's totalRestakedETH : 32 ETHER
    convertToAssets(balanceOf(Alice)) : 32 ETHER
    Bob's totalRestakedETH : 32 ETHER
    convertToAssets(balanceOf(Bob)) : 28 ETHER

In the described scenario, Alice successfully avoids slashing, while Bob incurs an additional loss of 2 ETH.

## Tools Used
H-04 Unmitigated
Audit: 2024-09-karak-mitigation-findings
Severity: HIGH

# Lines of code

https://github.com/karak-network/karak-restaking/tree/v2/src/entities/SlasherLib.sol#L116-L150


# Vulnerability details

## Impact

Operators can avoid slashing.

## Proof of Concept

As you can see from lines 126 to 131, the `SlasherLib.finalizeSlashing()` function skips unstaked vaults. Operators can exploit this to avoid slashing.

```solidity
    function finalizeSlashing(CoreLib.Storage storage self, QueuedSlashing memory queuedSlashing) internal {
        ...

        for (uint256 i = 0; i < queuedSlashing.vaults.length; i++) {
            if (
126             !self.operatorState[queuedSlashing.operator].isVaultStakedToDSS(
                    queuedSlashing.dss, queuedSlashing.vaults[i]
                )
            ) {
                emit SkippedSlashing(queuedSlashing.vaults[i]);
131             continue;
            }
            ...
```

Let's consider the following scenario:

1. The operator calls the [Core.requestUpdateVaultStakeInDSS()](https://github.com/karak-network/karak-restaking/tree/v2/src/Core.sol#L138) function to unstake `Vault 1` from DSS, initiating the 9-day `MIN_STAKE_UPDATE_DELAY`.
2. After 8 days, the DSS calls the [Core.requestSlashing()](https://github.com/karak-network/karak-restaking/tree/v2/src/Core.sol#L233) function on the operator’s `Vault 1`, starting the 2-day `SLASHING_VETO_WINDOW`.
3. After 1 day, the operator calls the [Core.finalizeUpdateVaultStakeInDSS()](https://github.com/karak-network/karak-restaking/tree/v2/src/Core.sol#L154) function to complete the unstaking of `Vault 1` from DSS.
4. After another day, the DSS (or anyone) calls the [Core.finalizeSlashing()](https://github.com/karak-network/karak-restaking/tree/v2/src/Core.sol#L261) function to finalize the slashing of the operator's `Vault 1`.

However, the slashing will skip `Vault 1` since the vault has already been unstaked from the DSS.

## Tools Used

Manual review

## Recommended Mitigation Steps

It is recommended to disallow unstaking vaults that have a pending slashing.


## Assessed type

Other
H-02 Unmitigated
Audit: 2024-09-karak-mitigation-findings
Severity: HIGH

# Lines of code

https://github.com/karak-network/karak-arena-mitigations/blob/475cfd73744cabe239720feec4a227a739910119/src/Core.sol#L171-L178
https://github.com/karak-network/karak-arena-mitigations/blob/475cfd73744cabe239720feec4a227a739910119/src/entities/CoreLib.sol#L118-L147
https://github.com/karak-network/karak-arena-mitigations/blob/475cfd73744cabe239720feec4a227a739910119/src/entities/CoreLib.sol#L89-L116
https://github.com/karak-network/karak-arena-mitigations/blob/475cfd73744cabe239720feec4a227a739910119/src/NativeVault.sol#L46-L77


# Vulnerability details

## C4 Issue: 
H-02: https://github.com/code-423n4/2024-07-karak-findings/issues/55

## Comments
Before mitigation when deploying a new NativeVault operator could set `manager`, `slashStore` and `nodeImplementation` to anything. This happened because `vaultConfig.extraData` lacked input validation when calling `deployVaults()`. The impacts of the original issue were the ability of operator to create a NativeVault that can be silently unslashable and escalate his rights, obtaining a privileged role that will allow him to steal user's funds. 

## Mitigation
[FIX](https://github.com/karak-network/karak-arena-mitigations/commit/fdef9d25e2b7c0a528d5a6dfcce64a3a518165af#diff-940446432243a929cd0f5ea691c4e90d60ee655723e2d5d8fcafc7b7504cfe98L59-R61)
The mitigation removes the `slashStore` variable altogether and `NativeVault` burns the slashed ETH directly by itself. The current mitigation does not address all of the impacts of the original issue. As currently operator can still escalate his rights and steal user's funds.

## Proof Of Concept
As can be seen, when `deployVaults()` is called with `vaultConfigs` passed by an operator, the variables is passed to `createVault()` unvalidated.

[CoreLib.sol#L133-L141](https://github.com/karak-network/karak-arena-mitigations/blob/475cfd73744cabe239720feec4a227a739910119/src/entities/CoreLib.sol#L133-L141)
```solidity
IKarakBaseVault vault = createVault(
    self,
    operator,
    vaultConfigs[i].asset,
    vaultConfigs[i].name,
    vaultConfigs[i].symbol,
    vaultConfigs[i].extraData,
    implementation
);
```

After cloning a vault, `createVault` initializes it with the configuration passed by the operator.

[CoreLib.sol#L107](https://github.com/karak-network/karak-arena-mitigations/blob/475cfd73744cabe239720feec4a227a739910119/src/entities/CoreLib.sol#L107)
```solidity
 vault.initialize(address(this), operator, depositToken, name, symbol, extraData);
```

When `initialize()` is called, the `extraData` parameter is decoded into `manager` and `nodeImplementation` which are only validated to not be `address(0)`.

[NativeVault.sol#L59-L62](https://github.com/karak-network/karak-arena-mitigations/blob/475cfd73744cabe239720feec4a227a739910119/src/NativeVault.sol#L59-L62)
```solidity
(address manager, address nodeImplementation) = abi.decode(
    _extraData,
    (address, address)
);
if (manager == address(0) || nodeImplementation == address(0))
    re
H-02 Unmitigated
Audit: 2024-09-karak-mitigation-findings
Severity: HIGH

# Lines of code

https://github.com/karak-network/karak-arena-mitigations/blob/475cfd73744cabe239720feec4a227a739910119/src/NativeVault.sol#L81


# Vulnerability details

### H-02:**The operator can create a `NativeVault` that can be silently unslashable.**

[Link to issue](https://github.com/code-423n4/2024-07-karak-findings/issues/55)

## **Comments**

The original implementation allows operators to set `manager`, `slashStore`, and `nodeImplementation` to arbitrary values. This flexibility enables operators to create an unslashable vault by setting the `slashStore` to an address other than the whitelisted slashing handler for ETH

## **Mitigation**

 [Fix link](https://github.com/karak-network/karak-arena-mitigations/commit/fdef9d25e2b7c0a528d5a6dfcce64a3a518165af#diff-940446432243a929cd0f5ea691c4e90d60ee655723e2d5d8fcafc7b7504cfe98R429)

The mitigation does not fully address all potential risks:

- The `slashStore` issue has been correctly handled by removing the component.
- The initial `nodeImplementation` appears acceptable, as users and the DSS owner can review it before joining the vault.

However, a vulnerability remains with the `manager` role. The manager, designated by the operator during deployment, retains the ability to call the `changeNodeImplementation` function and change the implementation to anything without delay. 

This setup could lead to a scenario where a vault, initially functioning correctly and trusted by users, becomes rogue as the operator or manager updates the implementation to a malicious version without warning.

```solidity
    /// @notice Allows the owner to change the NativeNode implementation and upgrade all NativeNodes
    /// @param newNodeImplementation: The address of the new node implementation
    function changeNodeImplementation(address newNodeImplementation)
        external
        onlyOwnerOrRoles(Constants.MANAGER_ROLE)
        whenFunctionNotPaused(Constants.PAUSE_NATIVEVAULT_NODE_IMPLEMENTATION)
    {
        if (newNodeImplementation == address(0)) revert ZeroAddress();

        _state().nodeImpl = newNodeImplementation;
        emit UpgradedAllNodes(newNodeImplementation);
    }
```

## **Recommended additional mitigation:**

A viable solution to further protect users is to modify the `changeNodeImplementation` function to incorporate a request-and-finalize change process, where any proposed changes to the node implementation must go through a time-locked approval phase. This approach allows users who disagree with the proposed changes or detect a potentially malicious implementation to withdraw their funds before the update is finalized. 

- The time lock should exceed the withdrawal time lock, providing users sufficient time to exit safely.

This solution allows operators to update the node implementation while protecting users from unexpected and potentially harmful changes, maintaining trust and security within the system.
M-02 Unmitigated
Audit: 2024-09-karak-mitigation-findings
Severity: MEDIUM

# Lines of code

https://github.com/karak-network/karak-restaking/tree/v2/src/entities/NativeVault.sol#L509-L517


# Vulnerability details

## Impact

The exchange rate is adjusted unfairly in the `_decreaseBalance` function when a node owner lacks sufficient shares to burn.

## Proof of Concept

When decreasing balance of a node owner, he may have fewer shares than what should be burned. In this case, all of his shares are burned. However, `totalAssets` is still reduced by the original amount of assets, so the subtracted amount from `totalAssets` is larger than the amount corresponding to the actually burning shares.
As a result, the exchange rate is changed unfairly, resulting in the loss of fund to remaining users.

```solidity
    function _decreaseBalance(address _of, uint256 assets) internal {
        NativeVaultLib.Storage storage self = _state();
511:    uint256 shares = Math.min(convertToShares(assets), balanceOf(_of));
512:    _beforeWithdraw(assets, shares);
513:    _burn(_of, shares);
        self.totalAssets -= assets;
515:    self.ownerToNode[_of].totalRestakedETH -= assets;
        emit DecreasedBalance(self.ownerToNode[_of].totalRestakedETH);
    }
```

Consider the following scenario:
1. Alice add a validator with 32 ETH and successfully complete a snapshot, and Bob adds two validators with 32 ETH and successfully complete a snapshot.

    totalAssets : 96

2. Slashing of 6 ETHER occurs.

    totalAssets : 96 - 6 = 90 ETHER
    convertToAssets(balanceOf(Alice)) : 30 ETHER
    convertToAssets(balanceOf(Bob)) : 60 ETHER

3. Alice's validator loses all its funds, so `totalDeltaWei` is -32 ETHER.

4. A snapshot is done for Alice's node.
    
    totalDeltaWei : -32 ETHER
    totalAssets : 90 - 32 = 58 ETHER
    convertToAssets(balanceOf(Bob)) : 58 ETHER (Bob has all remaining shares.)

In the described scenario, Bob incurs an additional loss of 60 - 58 = 2 ETHER (plus 2 ETHER from slashing).

## Tools Used

Manual review

## Recommended Mitigation Steps

Only the amount corresponding to burning shares should be subtracted from `totalAssets`.

```diff
    function _decreaseBalance(address _of, uint256 assets) internal {
        NativeVaultLib.Storage storage self = _state();
-       uint256 shares = Math.min(convertToShares(assets), balanceOf(_of));
+       uint256 shares = convertToShares(assets);
+       if (shares > balanceOf(_of)) {
+           shares = balanceOf(_of);
+           self.totalAssets -= convertToAssets(shares);
+       }
+       else {
+           self.totalAssets -= assets;
+       }
        _beforeWithdraw(assets, shares);
        _burn(_of, shares);
-       self.totalAssets -= assets;
        self.ownerToNode[_of].totalRestakedETH -= assets;
        emit DecreasedBalance(self.ownerToNode[_of].totalRestakedETH);
    }
```


## Assessed type

Other
Stakers in the NativeVault could be unfairly slashed.
Audit: 2024-09-karak-mitigation-findings
Severity: MEDIUM

# Lines of code

https://github.com/karak-network/karak-arena-mitigations/blob/475cfd73744cabe239720feec4a227a739910119/src/NativeVault.sol#L509-L517


# Vulnerability details

## Bug description
> Note: Even though the root cause of the issue was not discovered in the previous audit, the presented scenario is the same as in [M-02](https://github.com/code-423n4/2024-07-karak-findings/issues/31).

Consider a scenario where both Alice and Bob each have 32 ETH restaked into the NativeVault. NativeVault's `totalAssets` equals 64 ETH. A slashing event occurs in the vault, resulting in the NativeVault being slashed by 2 ETH, reducing `totalAssets` to 62 ETH.

[NativeVault.sol#L312](https://github.com/karak-network/karak-arena-mitigations/blob/475cfd73744cabe239720feec4a227a739910119/src/NativeVault.sol#L312)
```solidity
self.totalAssets -= totalAssetsToSlash;
```

Now 32 shares of both users amount to `32 * 62 / 64 = 31 ETH`, meaning that both Alice and Bob lost 1 ETH due to the slashing. After a slashing event has occurred in the NativeVault, Alice's validator looses all of its funds. Snapshot is started for Alice to reduce her assets by 32 ETH. `validateSnapshotProofs()` will calculate `balanceDeltaWei` as -32, subsequently calling `_updateSnapshot()` function.

[NativeVault.sol#L151-L159](https://github.com/karak-network/karak-arena-mitigations/blob/475cfd73744cabe239720feec4a227a739910119/src/NativeVault.sol#L151-L159)
```solidity
    int256 balanceDeltaWei = self.validateSnapshotProof(
        nodeOwner,
        validatorDetails,
        balanceContainer.containerRoot,
        balanceProofs[i]
    );
    snapshot.remainingProofs--;
    snapshot.balanceDeltaWei += balanceDeltaWei;
}
_updateSnapshot(node, snapshot, nodeOwner);
```

`_updateSnapshot()` calls `_updateBalance()`, where `_decreaseBalance()` function is invoked. `_decreaseBalance()` will burn all of Alice's shares and reduce `totalAssets` of the NativeVault by 32 ETH.

[NativeVault.sol#L511-L515](https://github.com/karak-network/karak-arena-mitigations/blob/475cfd73744cabe239720feec4a227a739910119/src/NativeVault.sol#L511-L515)
```solidity
uint256 shares = Math.min(convertToShares(assets), balanceOf(_of));
_beforeWithdraw(assets, shares);
_burn(_of, shares);
self.totalAssets -= assets;
self.ownerToNode[_of].totalRestakedETH -= assets;
```
After transaction has been completed, `totalAssets` are reduced to 30 ETH, leaving Bob only being able to withdraw 30 ETH, even though he should be able to withdraw 31.
## Impact
Loss of funds for a user.

## Recommended Mitigation
`_decreaseBalance()` function should calculate the amount of assets to slash based on  the amount of shares of a user.
```diff
function _decreaseBalance(address _of, uint256 assets) internal {
    NativeVaultLib.Storage storage self = _state();
    uint256 shares = Math.min(convertToShares(assets), balanceOf(_of));
+   uint256 assetsToSlash = Math.min(assets, convertToAssets(balanceOf(_of)));
-   _beforeWithdraw(assets, share