xanoutas/solidity-security-findings huggingface dataset:
Inability to Remove Compromised Owners Due to Data Loss and Lack of Backup Records
Audit: 2024-04-coinbase-mitigation-findings
Severity: HIGH

# Lines of code

https://github.com/coinbase/smart-wallet/blob/95c8ab2777fa69243a56a5f4d1ce8ce629d707f3/src/MultiOwnable.sol#L109


# Vulnerability details

## Impact

The vulnerability arises specifically because the MultiOwnable contract's security model does not account for the real-world possibility of data loss. It assumes that owners will always have access to and the ability to reproduce the exact bytes representing their public keys.

An owner who has lost control over their private key will remain listed. This could leave the door open for attackers if the private key is compromised, as the legitimate participants would have no means to revoke the compromised owner's access.

[see code below:](https://github.com/coinbase/smart-wallet/blob/95c8ab2777fa69243a56a5f4d1ce8ce629d707f3/src/MultiOwnable.sol#L109)

```solidity
function removeOwnerAtIndex(uint256 index, bytes calldata owner_) public virtual onlyOwner {
        bytes memory owner = ownerAtIndex(index);
        if (owner.length == 0) revert NoOwnerAtIndex(index);
        if (keccak256(owner) != keccak256(owner_)) revert WrongOwnerAtIndex(index, owner_);

        delete _getMultiOwnableStorage().isOwner[owner];
        delete _getMultiOwnableStorage().ownerAtIndex[index];

        emit RemoveOwner(index, owner);
    }
```

In the case where data is not managed efficiently through use of backups and publicly accessible accurate records it might be impossible to remove owner hence compromising the function utility and ownership all together.

## Poc Scenario
- Owner A and Owner B are added using their Ethereum addresses.
- Owner C is added using their public key, represented by (x, y) coordinates encoded as bytes, to utilize the passkey feature for additional security measures.
- Owners A and B are registered with their Ethereum addresses.
- Owner C generates a new Ethereum key pair specifically for this contract, uses the public key derived from this pair for registration, and is added with the public key bytes.
- Owner C loses the device containing their private key. This loss includes the inability to access the Ethereum account and, critically, the inability to regenerate the exact public key bytes used for their registration as an owner in the `MultiOwnable` contract.
- Due to the loss of the private key, Owner C can no longer participate in the governance of the DeFi application. The remaining owners decide to remove Owner C from the contract.
- The contract requires the exact bytes of Owner C's public key for removal. However, without access to the private key or an external, accurate record of the public key bytes, the remaining owners cannot generate the required data to invoke `removeOwnerAtIndex` successfully.
- Owner C remains a registered owner, despite no longer having control over their associated private key. This situation presents a serious security risk and operational challenge, as Owner C can be potentially compromised and can't be removed.

## Severity Assesment:
M-01 Unmitigated
Audit: 2024-04-coinbase-mitigation-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L130-L135


# Vulnerability details

## Issue Report
M-01: Balance check during MagicSpend validation cannot ensure that MagicSpend has enough balance to cover the requested fund.

## Details

[Issue#110](https://github.com/code-423n4/2024-03-coinbase-findings/issues/110)

An issue was identified in MagicSpend contract where during the validation phase of multiple UserOperations, the contract's balance check does not account for the cumulative effect of multiple withdrawals being validated and executed in sequence. This can lead to a situation where the contract believes it has enough balance to cover all requests during validation but runs out of funds during the execution phase, due to not accounting for the total aggregate withdrawal amount requested by all operations.

## Mitigation
1. [PR#16](https://github.com/coinbase/magic-spend/pull/16)
2. [PR#17](https://github.com/coinbase/magic-spend/pull/17)

In the first [PR](https://github.com/coinbase/magic-spend/pull/16) the core idea was to use a variable to keep track of the total amount being withdrawn during the validation phase of each UserOperation within a transaction bundle. This would help ensure that the sum of all withdrawals did not exceed the contract's available balance. By validating each withdrawal request against the tracked total withdrawal amount and the contract's balance, the PR aimed to preemptively reject withdrawal requests that would lead to an insufficient balance, thus preventing the entire transaction bundle from failing. This however turns out to be inefficient in the case of paymaster as reputional damage will have already been done by the multiple reverts introduced by this mitigation.

The second [PR](https://github.com/coinbase/magic-spend/pull/17) introduced a new strategy to address the issue by setting a maxWithdrawDenominator, which represents a more probabilistic approach to managing the risk of transaction reverts due to insufficient contract balance when multiple withdrawals occur within the same transaction bundle. By probabilistically limiting withdrawal amounts, the strategy reduces the likelihood of transaction reverts in bundles due to insufficient balance, which could negatively impact the Paymaster's reputation.

The [issue#110](https://github.com/code-423n4/2024-03-coinbase-findings/issues/110) is not entirely resolved but the introduction of maxWithdrawDenominator allows for limiting the size of each withdrawal relative to the contract's balance. This approach is designed to probabilistically reduce the likelihood of reverts due to balance insufficiency when multiple withdrawals are processed in the same transaction.

[Loc](https://github.com/coinbase/magic-spend/blob/fc38b23379eeee9c5babf0289e1a634935e38ee1/src/MagicSpend.sol#L344)

## Suggestion
Implement mechanisms for dynamically adjusting the maxWithdrawDe
amount requires to be updated to contract balance increase (18)
Audit: 2022-05-alchemix-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-05-alchemix/blob/71abbe683dfd5c0686b7e594fb4f78a14b668d8b/contracts-full/AutoleverageCurveMetapool.sol#L14-L17


# Vulnerability details

## Impact
Every time transferFrom or transfer function in ERC20 standard is called there is a possibility that underlying smart contract did not transfer the exact amount entered.
It is required to find out contract balance increase/decrease after the transfer.
This pattern also prevents from re-entrancy attack vector.
Also make sure to use SafeERC20.

## Proof of Concept

## Tools Used

## Recommended Mitigation Steps
Recommended code:
```solidity
function _transferTokensToSelf(address underlyingToken, uint256 collateralInitial) internal override {
    if (msg.value > 0) revert IllegalArgument("msg.value should be 0");

    uint256 balanceBefore =  IERC20(underlyingToken).balanceOf(address(this)); // remembering asset balance before the transfer
    IERC20(underlyingToken).safeTransferFrom(msg.sender, address(this), collateralInitial); // make sure to use SafeERC20
    uint256 newCollateralInitial =  IERC20(underlyingToken).balanceOf(address(this)) - balanceBefore; // updating actual amount to the contract balance increase

    // no re-entrancy attack vector below here

    require(newCollateralInitial == collateralInitial); // making sure we received correct amount
}
```
amount requires to be updated to contract balance increase (17)
Audit: 2022-05-alchemix-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-05-alchemix/blob/71abbe683dfd5c0686b7e594fb4f78a14b668d8b/contracts-full/AutoleverageCurveFactoryethpool.sol#L20-L28


# Vulnerability details

## Impact
Every time transferFrom or transfer function in ERC20 standard is called there is a possibility that underlying smart contract did not transfer the exact amount entered.
It is required to find out contract balance increase/decrease after the transfer.
This pattern also prevents from re-entrancy attack vector.
Also make sure to use SafeERC20.
If this function is meant to be used for WETH only, then consider adding require to avoid other tokens deposits.
And finally reduce if nesting.

## Proof of Concept

## Tools Used

## Recommended Mitigation Steps
Recommended code:
```solidity
function _transferTokensToSelf(address underlyingToken, uint256 collateralInitial) internal override {
    // Convert eth to weth if received eth, otherwise transfer weth
    if (msg.value > 0) {
        if (msg.value != collateralInitial) revert IllegalArgument("msg.value doesn't match collateralInitial");
        IWETH9(wethAddress).deposit{value: msg.value}();
        return;
    }

    // reduced if nesting
    uint256 balanceBefore =  IERC20(underlyingToken).balanceOf(address(this)); // remembering asset balance before the transfer
    IERC20(underlyingToken).safeTransferFrom(msg.sender, address(this), collateralInitial); // make sure to use SafeERC20
    uint256 newCollateralInitial =  IERC20(underlyingToken).balanceOf(address(this)) - balanceBefore; // updating actual amount to the contract balance increase

    // no re-entrancy attack vector below here

    require(newCollateralInitial == collateralInitial); // making sure we received correct amount  

}
```
amount requires to be updated to contract balance increase (15)
Audit: 2022-05-alchemix-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-05-alchemix/blob/71abbe683dfd5c0686b7e594fb4f78a14b668d8b/contracts-full/StakingPools.sol#L375-L385


# Vulnerability details

## Impact
Every time transferFrom or transfer function in ERC20 standard is called there is a possibility that underlying smart contract did not transfer the exact amount entered.
It is required to find out contract balance increase/decrease after the transfer.
This pattern also prevents from re-entrancy attack vector.

## Proof of Concept

## Tools Used

## Recommended Mitigation Steps
Recommended code:
```solidity
function _deposit(uint256 _poolId, uint256 _depositAmount) internal {
    Pool.Data storage _pool = _pools.get(_poolId);
    Stake.Data storage _stake = _stakes[msg.sender][_poolId];

    _pool.totalDeposited = _pool.totalDeposited + _depositAmount;
    _stake.totalDeposited = _stake.totalDeposited + _depositAmount;

    uint256 balanceBefore =  _pool.token.balanceOf(address(this)); // remembering asset balance before the transfer
    _pool.token.safeTransferFrom(msg.sender, address(this), _depositAmount);
    _depositAmount =  _pool.token.balanceOf(address(this)) - balanceBefore; // updating actual amount to the contract balance increase

    // no re-entrancy attack vector below here

    emit TokensDeposited(msg.sender, _poolId, _depositAmount);
}
```
amount requires to be updated to contract balance increase (14)
Audit: 2022-05-alchemix-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-05-alchemix/blob/71abbe683dfd5c0686b7e594fb4f78a14b668d8b/contracts-full/TransmuterConduit.sol#L34-L37


# Vulnerability details

## Impact
Every time transferFrom or transfer function in ERC20 standard is called there is a possibility that underlying smart contract did not transfer the exact amount entered.
It is required to find out contract balance increase/decrease after the transfer.
This pattern also prevents from re-entrancy attack vector.

## Proof of Concept

## Tools Used

## Recommended Mitigation Steps
Recommended code:
```solidity
function distribute(address origin, uint256 amount) external onlySource() {
    uint256 balanceBefore = IERC20(origin).balanceOf(sinkTransmuter); // remembering asset balance before the transfer
    IERC20(token).safeTransferFrom(origin, sinkTransmuter, amount);
    amount = IERC20(origin).balanceOf(sinkTransmuter) - balanceBefore; // updating actual amount to the contract balance increase

    // no re-entrancy attack vector below here    
    
    IERC20TokenReceiver(sinkTransmuter).onERC20Received(token, amount);
}
```
amount requires to be updated to contract balance increase (13)
Audit: 2022-05-alchemix-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-05-alchemix/blob/71abbe683dfd5c0686b7e594fb4f78a14b668d8b/contracts-full/adapters/vesper/VesperAdapterV1.sol#L80-L106


# Vulnerability details

## Impact
Every time transferFrom or transfer function in ERC20 standard is called there is a possibility that underlying smart contract did not transfer the exact amount entered.
It is required to find out contract balance increase/decrease after the transfer.
This pattern also prevents from re-entrancy attack vector.

## Proof of Concept

## Tools Used

## Recommended Mitigation Steps
Recommended code:
```solidity
function unwrap(
    uint256 amount,
    address recipient
) external lock onlyAlchemist returns (uint256) {
    // Transfer the tokens from the message sender.
    uint256 balanceBefore = IERC20(token).balanceOf(address(this)); // remembering asset balance before the transfer
    SafeERC20.safeTransferFrom(token, msg.sender, address(this), amount);
    amount = IERC20(token).balanceOf(address(this)) - balanceBefore; // updating actual amount to the contract balance increase

    // no re-entrancy attack vector below here    

    uint256 balanceBeforeUnderlying = IERC20(underlyingToken).balanceOf(address(this));
    uint256 balanceBeforeYieldToken = IERC20(token).balanceOf(address(this));
    
    // Vesper withdraw does not accept a recipient argument and does not return withdrawn amount
    IVesperPool(token).withdraw(amount);

    uint256 balanceAfterUnderlying = IERC20(underlyingToken).balanceOf(address(this));
    uint256 balanceAfterYieldToken = IERC20(token).balanceOf(address(this));

    uint256 withdrawn = balanceAfterUnderlying - balanceBeforeUnderlying;

    if (balanceBeforeYieldToken - balanceAfterYieldToken != amount) {
        revert IllegalState("Not all shares were burned");
    }

    // We must transfer to recipient after and use IERC20.balanceOf() for amount
    SafeERC20.safeTransfer(underlyingToken, recipient, withdrawn);

    return withdrawn;
}
```
amount requires to be updated to contract balance increase (12)
Audit: 2022-05-alchemix-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-05-alchemix/blob/71abbe683dfd5c0686b7e594fb4f78a14b668d8b/contracts-full/adapters/vesper/VesperAdapterV1.sol#L56-L77


# Vulnerability details

## Impact
Every time transferFrom or transfer function in ERC20 standard is called there is a possibility that underlying smart contract did not transfer the exact amount entered.
It is required to find out contract balance increase/decrease after the transfer.
This pattern also prevents from re-entrancy attack vector.

## Proof of Concept

## Tools Used

## Recommended Mitigation Steps
Recommended code:
```solidity
function wrap(
    uint256 amount,
    address recipient
) external onlyAlchemist returns (uint256) {
    // Transfer the underlying tokens from the message sender.
    uint256 balanceBefore = IERC20(underlyingToken).balanceOf(address(this)); // remembering asset balance before the transfer
    SafeERC20.safeTransferFrom(underlyingToken, msg.sender, address(this), amount);
    amount = IERC20(underlyingToken).balanceOf(address(this)) - balanceBefore; // updating actual amount to the contract balance increase

    // no re-entrancy attack vector below here

    SafeERC20.safeApprove(underlyingToken, token, amount);

    balanceBefore = IERC20(token).balanceOf(address(this));

    // Vesper deposit does not accept a recipient argument and does not return mint amount
    IVesperPool(token).deposit(amount);

    uint256 balanceAfter = IERC20(token).balanceOf(address(this));

    uint256 minted = balanceAfter - balanceBefore;

    // We must transfer to recipient after and use IERC20.balanceOf() for amount
    SafeERC20.safeTransfer(token, recipient, minted);

    return minted;
}
```
amount requires to be updated to contract balance increase (11)
Audit: 2022-05-alchemix-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-05-alchemix/blob/71abbe683dfd5c0686b7e594fb4f78a14b668d8b/contracts-full/adapters/rocket/RETHAdapterV1.sol#L83-L102


# Vulnerability details

## Impact
Every time transferFrom or transfer function in ERC20 standard is called there is a possibility that underlying smart contract did not transfer the exact amount entered.
It is required to find out contract balance increase/decrease after the transfer.
This pattern also prevents from re-entrancy attack vector.

## Proof of Concept

## Tools Used

## Recommended Mitigation Steps
Recommended code:
```solidity
function unwrap(
    uint256 amount,
    address recipient
) external lock onlyAlchemist returns (uint256) {
    // Transfer the rETH from the message sender.
    uint256 balanceBefore = IERC20(token).balanceOf(address(this)); // remembering asset balance before the transfer
    SafeERC20.safeTransferFrom(token, msg.sender, address(this), amount);
    amount = IERC20(token).balanceOf(address(this)) - balanceBefore; // updating actual amount to the contract balance increase

    // no re-entrancy attack vector below here    

    // Burn the rETH to receive ETH.
    uint256 startingEthBalance = address(this).balance;
    IRETH(token).burn(amount);
    uint256 receivedEth = address(this).balance - startingEthBalance;

    // Wrap the ETH that we received from the burn.
    IWETH9(underlyingToken).deposit{value: receivedEth}();

    // Transfer the tokens to the recipient.
    SafeERC20.safeTransfer(underlyingToken, recipient, receivedEth);

    return receivedEth;
}
```
amount requires to be updated to contract balance increase (10)
Audit: 2022-05-alchemix-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-05-alchemix/blob/71abbe683dfd5c0686b7e594fb4f78a14b668d8b/contracts-full/adapters/lido/WstETHAdapterV1.sol#L120


# Vulnerability details

## Impact
Every time transferFrom or transfer function in ERC20 standard is called there is a possibility that underlying smart contract did not transfer the exact amount entered.
It is required to find out contract balance increase/decrease after the transfer.
This pattern also prevents from re-entrancy attack vector.

## Proof of Concept

## Tools Used

## Recommended Mitigation Steps
Recommended code:
```solidity
function unwrap(
    uint256 amount,
    address recipient
) external lock onlyAlchemist returns (uint256) {
    // Transfer the tokens from the message sender.
    uint256 balanceBefore = IERC20(token).balanceOf(address(this)); // remembering asset balance before the transfer
    SafeERC20.safeTransferFrom(token, msg.sender, address(this), amount);
    amount = IERC20(token).balanceOf(address(this)) - balanceBefore; // updating actual amount to the contract balance increase

    // no re-entrancy attack vector below here    

    // Unwrap the wstETH into stETH.
    uint256 startingStEthBalance = IStETH(parentToken).balanceOf(address(this));
    IWstETH(token).unwrap(amount);
    uint256 endingStEthBalance = IStETH(parentToken).balanceOf(address(this));

    // Approve the curve pool to transfer the tokens.
    uint256 unwrappedStEth = endingStEthBalance - startingStEthBalance;
    SafeERC20.safeApprove(parentToken, curvePool, unwrappedStEth);

    // Exchange the stETH for ETH. We do not check the curve pool because it is an immutable
    // contract and we expect that its output is reliable.
    uint256 received = IStableSwap2Pool(curvePool).exchange(
        int128(uint128(stEthPoolIndex)), // Why are we here, just to suffer?
        int128(uint128(ethPoolIndex)),   //                       (╥﹏╥)
        unwrappedStEth,
        0                                // <- Slippage is handled upstream
    );

    // Wrap the ETH that we received from the exchange.
    IWETH9(underlyingToken).deposit{value: received}();

    // Transfer the tokens to the recipient.
    SafeERC20.safeTransfer(underlyingToken, recipient, received);

    return received;
}
```
amount requires to be updated to contract balance increase (9)
Audit: 2022-05-alchemix-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-05-alchemix/blob/71abbe683dfd5c0686b7e594fb4f78a14b668d8b/contracts-full/adapters/lido/WstETHAdapterV1.sol#L87-L112


# Vulnerability details

## Impact
Every time transferFrom or transfer function in ERC20 standard is called there is a possibility that underlying smart contract did not transfer the exact amount entered.
It is required to find out contract balance increase/decrease after the transfer.
This pattern also prevents from re-entrancy attack vector.

## Proof of Concept

## Tools Used

## Recommended Mitigation Steps
Recommended code:
```solidity
function wrap(
    uint256 amount,
    address recipient
) external lock onlyAlchemist returns (uint256) {
    // Transfer the tokens from the message sender.
    uint256 balanceBefore = IERC20(underlyingToken).balanceOf(address(this)); // remembering asset balance before the transfer
    SafeERC20.safeTransferFrom(underlyingToken, msg.sender, address(this), amount);
    amount = IERC20(underlyingToken).balanceOf(address(this)) - balanceBefore; // updating actual amount to the contract balance increase

    // no re-entrancy attack vector below here

    // Unwrap the WETH into ETH.
    IWETH9(underlyingToken).withdraw(amount);

    // Wrap the ETH into stETH.
    uint256 startingStEthBalance = IERC20(token).balanceOf(address(this));

    IStETH(parentToken).submit{value: amount}(referral);

    uint256 mintedStEth = IERC20(parentToken).balanceOf(address(this)) - startingStEthBalance;

    // Wrap the stETH into wstETH.
    SafeERC20.safeApprove(parentToken, address(token), mintedStEth);
    uint256 mintedWstEth = IWstETH(token).wrap(mintedStEth);

    // Transfer the minted wstETH to the recipient.
    SafeERC20.safeTransfer(token, recipient, mintedWstEth);

    return mintedWstEth;
}
```
amount requires to be updated to contract balance increase (8)
Audit: 2022-05-alchemix-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-05-alchemix/blob/71abbe683dfd5c0686b7e594fb4f78a14b668d8b/contracts-full/adapters/fuse/FuseTokenAdapterV1.sol#L89-L108


# Vulnerability details

## Impact
Every time transferFrom or transfer function in ERC20 standard is called there is a possibility that underlying smart contract did not transfer the exact amount entered.
It is required to find out contract balance increase/decrease after the transfer.
This pattern also prevents from re-entrancy attack vector.

## Proof of Concept

## Tools Used

## Recommended Mitigation Steps
Recommended code:
```solidity
function unwrap(
    uint256 amount,
    address recipient
) external lock onlyAlchemist returns (uint256) {
    uint256 balanceBefore = IERC20(token).balanceOf(address(this)); // remembering asset balance before the transfer
    SafeERC20.safeTransferFrom(token, msg.sender, address(this), amount);
    amount = IERC20(token).balanceOf(address(this)) - balanceBefore; // updating actual amount to the contract balance increase

    // no re-entrancy attack vector below here

    uint256 startingBalance = IERC20(underlyingToken).balanceOf(address(this));

    uint256 error;
    if ((error = ICERC20(token).redeem(amount)) != NO_ERROR) {
        revert FuseError(error);
    }

    uint256 endingBalance = IERC20(underlyingToken).balanceOf(address(this));
    uint256 redeemedAmount = endingBalance - startingBalance;

    SafeERC20.safeTransfer(underlyingToken, recipient, redeemedAmount);

    return redeemedAmount;
}
```