xanoutas/solidity-security-findings huggingface dataset:
Rounding error in `WUSDA` can result in loss of user funds, especially when manipulated by an attacker
Audit: 2023-07-amphora-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/WUSDA.sol#L247


# Vulnerability details

## Impact
Due to floor rounding in Solidity, it is possible for rounding errors to affect functions in the `WUSDA` contract.

Specifically, when `WUSDA::_usdaToWUSDA` is invoked internally, if `_usdaAmount` is sufficiently small compared with `MAX_wUSDA_SUPPLY` (10M) and the total USDA supply then it is possible for the `_wusdaAmount` return value to round down to 0.

```solidity
function _usdaToWUSDA(uint256 _usdaAmount, uint256 _totalUsdaSupply) private pure returns (uint256 _wusdaAmount) {
    _wusdaAmount = (_usdaAmount * MAX_wUSDA_SUPPLY) / _totalUsdaSupply;
  }
```

When calling the `WUSDA::deposit/depositTo` functions, which call the internal `WUSDA::_deposit` function with this rounded `_wusdaAmount` value, the implication is that depositors could receive little/no WUSDA for their USDA. Additionally, calls to the `WUSDA::withdraw/withdrawTo` functions could be process without actually burning any WUSDA. This is clearly not desirable as USDA can be subsequently redeemed for sUSD (at the expense of the protocol reserves and its other depositors).

```solidity
function deposit(uint256 _usdaAmount) external override returns (uint256 _wusdaAmount) {
    _wusdaAmount = _usdaToWUSDA(_usdaAmount, _usdaSupply());
    _deposit(_msgSender(), _msgSender(), _usdaAmount, _wusdaAmount);
  }

function withdraw(uint256 _usdaAmount) external override returns (uint256 _wusdaAmount) {
    _wusdaAmount = _usdaToWUSDA(_usdaAmount, _usdaSupply());
    _withdraw(_msgSender(), _msgSender(), _usdaAmount, _wusdaAmount);
  }
```

Even when the USDA supply is sufficiently small to avoid this issue, an attacker could still utilize a sUSD flash loan to artificially inflate the USDA total supply which causes the down rounding, potentially front-running a victim such that their deposit is processed without minting any WUSDA and then withdrawing the inflated USDA supply to repay the flash loan.

## Proof of Concept
_normal case:_
* Total USDA supply is 10B.
* Alice calls `WUSDA::deposit` with `_usdaAmount` of 1e23 (10_000 tokens).
* `_wusdaAmount` is rounded down to 1e20/100 tokens (1e23 * 1e25 / 1e28 = 1e20).
* Alice receives 100 WUSDA rather than 10_000.
* In reality, the total supply of sUSD is around 40M, so a more realistic scenario assuming 10M total USDA supply may be that Alice deposits 10e18 and receives 1 WUSDA or 1/10th the expected amount (10e18 * 1e25 / 1e26 = 1e18).

_flash loan & attacker front-running case:_
* Total USDA supply is 1M.
* Alice calls `WUSDA::deposit` with `_usdaAmount` of 9.9e22 (99_000 tokens).
* Attacker front-runs this transaction and calls `WUSDA::deposit` with `_usdaAmount` of 3.3e22 (33_000 tokens).
* Attacker receives 33_000 WUSDA.
* Attacker takes out a sUSD flash loan of 39M.
* Attacker deposits 39M sUSD into USDA.
* Alice's transaction is included here, b
Use of incorrect access control modifier in  AMPHClaimer.sol
Audit: 2023-07-amphora-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/AMPHClaimer.sol#L117-L144
https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/interfaces/core/IAMPHClaimer.sol#L115-L131


# Vulnerability details

## Impact
AMPHClaimer.sol contract is used to exchange CVX and CRV at a fixed rate for AMPH. The issue here is the use of incorrect access control modifier on some critical functions. The functions are,

```Solidity
File: core/solidity/contracts/core/AMPHClaimer.sol

117   /// @notice Used by governance to change the vault controller
118   /// @param _newVaultController The new vault controller
119   function changeVaultController(address _newVaultController) external override onlyOwner {


      // Some code


125   /// @notice Used by governance to recover tokens from the contract
126   /// @param _token The token to recover
127   /// @param _amount The amount to recover
128   function recoverDust(address _token, uint256 _amount) external override onlyOwner {


      // Some code


134   /// @notice Used by governance to change the fee taken from the CVX reward
135   /// @param _newFee The new reward fee
136   function changeCvxRewardFee(uint256 _newFee) external override onlyOwner {


      // Some code


142   /// @notice Used by governance to change the fee taken from the CRV reward
143   /// @param _newFee The new reward fee
144   function changeCrvRewardFee(uint256 _newFee) external override onlyOwner {


      // Some code

```

The focus should be here at comments at L-117,L-125,L-134,L-142, here the comments states **```Used by governance```** the above functions **must be accessed by Governance only** but these functions have onlyOwner modifier which means the owner who deployed the contract will have direct control on such critical functions. However this is not correct per Natspec.

These function comments further verified from AMPHClaimer.sol interface, however in interface it is mentioned as Governance but not owner. This can be checked [here](https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/interfaces/core/IAMPHClaimer.sol#L115-L131)

**Per the Amphora Protocol documentation,
"The Governor contract has ownership control over all Amphora contracts."**

Documentation reference [link](https://amphora-protocol.gitbook.io/amphora-protocol/governance) 

I believe these critical functions must only be accessed by governance address.

## Proof of Concept
https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/AMPHClaimer.sol#L117-L144

https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/interfaces/core/IAMPHClaimer.sol#L115-L131

## Tools Used
Manual review

## Recommended Mitigation Steps
Add governance access control modifier on t
Vulnerability in the WUSDA Smart Contract that Allows for Flash Mint Manipulation
Audit: 2023-07-amphora-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/WUSDA.sol#L235-L256


# Vulnerability details

## Impact
When a user deposits USDA into the wUSDA contract, the actual amount of wUSDA minted is calculated based on the totalSupply of USDA and maxSupply of wUSDA. When burning the wUSDA and claiming back these USDA the amount is also based on on the totalSupply of USDA and maxSupply of wUSDA. A malicious actor could manipulate to his advantage the totalSupply of USDA by doing a flashloan of sUSD and depositing them into USDA and artificially inflate the `totalSupply()` of USDA.


## Proof of Concept

Bob has 1,000 sUSD that he deposits into USDA, which mints him 1,000 USDA. 
Bob then deposits 1,000 USDA when MAX_wUSDA_SUPPLY = 10,000,000 and _totalUsdaSupply = 100,000. Bob mints 10,000 wUSDA.
(1,000*10,000,000/1000,000 = 100,000)

Bob then flashloans 100,000 sUSD and converts them to USDA, increasing _totalUsdaSupply to 200,000.

When Bob withdraws his wUSDA, the calculation of _usdaToWithdraw would yield a value of 2,000 USDA 
, effectively doubling Bob's initial deposit.
 (100,000*200,000/10,000,000 = 2,000)



## Tools Used

manual review

## Recommended Mitigation Steps

Consider adding safeguards to limit the impact of large deposits or withdrawals on the overall system


## Assessed type

Math
get_virtual_price can be manipulated in curve
Audit: 2023-07-amphora-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol#L58
https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol#L52


# Vulnerability details

## Impact

The [readability reentrant error](https://chainsecurity.com/heartbreaks-curve-lp-oracles/) in curve will affect the `get_virtual_price`. Malicious attackers can manipulate the price by using flash loan + reentrant.


## Proof of Concept

The protocol takes into account the effects of reentry in some scenarios:
- https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol#L39-L41
- https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol#L73-L74

But in some scenarios the price is read directly, which creates an attack vector:
- https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol#L58
- https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol#L52

It is worth mentioning that the StableCurveLpOracle does not seem to be used directly, but via inheritance, which handles the reentry scenario correctly and does not cause problems. However, TriCrypto2Oracle is not processed and is subject to reentrant attacks.

## Tools Used

Manual review

## Recommended Mitigation Steps

Handle the issue of price reentrant attacks like other scenarios


## Assessed type

Reentrancy
Missing Pool ID Check in depositERC20 Function Allows Unintended Deposits
Audit: 2023-07-amphora-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-amphora/blob/5d1cea9410db5448760c834f001af04a72edf3e0/core/solidity/contracts/core/Vault.sol#L89-L111


# Vulnerability details

## Impact
The `depositERC20` function currently lacks a pool ID check, which could lead to unintended deposits into the wrong pool.

## Proof of Concept
Upon comparing `depositERC20` with other deposit logic in the project, it appears that this function lacks the necessary pool ID verification.

```solidity
function stakeCrvLPCollateral(address _tokenAddress) external override onlyMinter {
    uint256 _poolId = CONTROLLER.tokenPoolId(_tokenAddress);
    if (_poolId == 0) revert Vault_TokenCanNotBeStaked();
}

function depositERC20(address _token, uint256 _amount) external override onlyMinter {
    if (CONTROLLER.tokenId(_token) == 0) revert Vault_TokenNotRegistered();
    if (_amount == 0) revert Vault_AmountZero();
    SafeERC20Upgradeable.safeTransferFrom(IERC20Upgradeable(_token), _msgSender(), address(this), _amount);
    if (CONTROLLER.tokenCollateralType(_token) == IVaultController.CollateralType.CurveLPStakedOnConvex) {
        uint256 _poolId = CONTROLLER.tokenPoolId(_token);
        // If it's type CurveLPStakedOnConvex, then pool id can't be 0
        IBooster _booster = CONTROLLER.BOOSTER();
        if (isTokenStaked[_token]) {
            // ...
        } else {
            // ...
        }
    }
    // ...
}
```

## Recommended Mitigation Steps
To prevent deposits into unintended pools, a pool ID check should be added to the `depositERC20` function.

```solidity
function depositERC20(address _token, uint256 _amount) external override onlyMinter {
    if (CONTROLLER.tokenId(_token) == 0) revert Vault_TokenNotRegistered();
    if (_amount == 0) revert Vault_AmountZero();
    SafeERC20Upgradeable.safeTransferFrom(IERC20Upgradeable(_token), _msgSender(), address(this), _amount);
    uint256 _poolId = CONTROLLER.tokenPoolId(_token);
    require(_poolId != 0, "Vault_PoolIdZero"); // Add a pool ID check
    if (CONTROLLER.tokenCollateralType(_token) == IVaultController.CollateralType.CurveLPStakedOnConvex) {
        IBooster _booster = CONTROLLER.BOOSTER();
        if (isTokenStaked[_token]) {
            // ...
        } else {
            // ...
        }
    }
    // ...
}
```

By incorporating this check, the `depositERC20` function will ensure that the deposited tokens are directed to the intended pool based on the provided pool ID.





## Assessed type

Access Control
_migrateCollateralsFrom does not take into account repeated tokens inside the array _tokenAddresses when called inside the constructor
Audit: 2023-07-amphora-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/VaultController.sol#L252-L274


# Vulnerability details

## Impact
The function `_migrateCollateralsFrom` does not validate that it is not reading repeated tokens, thus it will be overwriting `tokensRegistered` to be more than the count of different ones. That is bad because functions like `vaultBorrowingPower` or its peek variant will do a linear search over the array of registered tokens and it will return a wrong value due to double/x counting the tokens as they appear and it won't show the real state of the vault (its borrowing power will be higher than in reality, which can incur in undefined behavior when calling functions that depends on the return value of `_migrateCollateralsFrom`, e.g., calls to `checkVault` would lead to saying the vault is over-collateralized when it isn't, thus the high severity)

## Proof of Concept
From the code below, we can see that it does not take into account repeated ones

```
function _migrateCollateralsFrom(IVaultController _oldVaultController, address[] memory _tokenAddresses) internal {
    uint256 _tokenId;
    uint256 _tokensRegistered;
    for (uint256 _i; _i < _tokenAddresses.length;) {
      _tokenId = _oldVaultController.tokenId(_tokenAddresses[_i]); // @audit re-reads the same value if tokens repeated
      if (_tokenId == 0) revert VaultController_WrongCollateralAddress();
      _tokensRegistered++; // @audit and updates regardless

      CollateralInfo memory _collateral = _oldVaultController.tokenCollateralInfo(_tokenAddresses[_i]);
      _collateral.tokenId = _tokensRegistered;
      _collateral.totalDeposited = 0;

      enabledTokens.push(_tokenAddresses[_i]); // @audit push even repeated ones
      tokenAddressCollateralInfo[_tokenAddresses[_i]] = _collateral; // @audit overwrites with the latest info from the repeated token, e.g. the _collateral.tokenId for all tokens is not in the range [1..N_TOKENS]

      unchecked {
        ++_i;
      }
    }
    tokensRegistered += _tokensRegistered;

    emit CollateralsMigratedFrom(_oldVaultController, _tokenAddresses);
  }
```

So the `_getVaultBorrowingPower` will return a higher value than the real one and can lead to `checkVault` to return that the vault is over-collaterized when it may not

```
function checkVault(uint96 _id) public returns (bool _overCollateralized) {
    // grab the vault by id if part of our system. revert if not
    IVault _vault = _getVault(_id);
    // calculate the total value of the vault's liquidity
    uint256 _totalLiquidityValue = _getVaultBorrowingPower(_vault); <======= direct dependence
    // calculate the total liability of the vault
    uint256 _usdaLiability = _truncate((_vault.baseLiability() * interest.factor));
    // if the ltv >= liability, the vault is solvent
    _overCollateralized = (_totalLiquidityValue >= _usdaLiability); <======== artificial manipulation
  }
```
Potential Cheating Risks in Voting Process
Audit: 2023-07-amphora-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/governance/GovernorCharlie.sol#L616
https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/governance/GovernorCharlie.sol#L605


# Vulnerability details

## Impact
By having the ability to modify governance parameters, the contract owner gains an unfair advantage in decision-making. 
They can set voting periods and delays that align with their preferences, potentially pushing through proposals that benefit their interests or agenda. 
This can lead to decisions that are not in the best interest of the broader community or other stakeholders. 
As a result, users may feel marginalized or disenfranchised, leading to a breakdown of trust and participation in the governance process.

## Proof of Concept

    function setVotingPeriod(uint256 _newVotingPeriod) external override onlyGov {
    uint256 _oldVotingPeriod = votingPeriod;
    votingPeriod = _newVotingPeriod;

    emit VotingPeriodSet(_oldVotingPeriod, votingPeriod);
    }

    function setVotingDelay(uint256 _newVotingDelay) external override onlyGov {
    uint256 _oldVotingDelay = votingDelay;
    votingDelay = _newVotingDelay;

    emit VotingDelaySet(_oldVotingDelay, votingDelay);
    }


## Tools Used
Manual review

## Recommended Mitigation Steps
Apply time-locks to critical governance changes. Require a waiting period (e.g., 24 hours or more) between the approval of a proposal and its execution. 
During this time, the community can review the proposal and raise any concerns.


## Assessed type

Rug-Pull
Potential Rugpull Risk Due to Excessive Fee Settings
Audit: 2023-07-amphora-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/VaultController.sol#L681
https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/VaultController.sol#L435
https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/VaultController.sol#L425
https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/VaultController.sol#L533
https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/VaultController.sol#L686
https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/VaultController.sol#L637
https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/VaultController.sol#L321


# Vulnerability details

## Impact
The smart contract code exposes a potential rugpull risk, as the contract owner can set fees to excessively high values close to 1e18 afer initiating to low values .
 This vulnerability affects functions like changeInitialBorrowingFee, changeLiquidationFee, and changeProtocolFee, allowing the owner to impose exorbitant fees on users.
 The impact includes discouraging user participation, leading to potential financial losses during liquidation, and overcharging users for protocol interactions.
 Additionally, the inaccurate liquidation simulation results may misguide risk assessment.

## Proof of Concept

    function changeInitialBorrowingFee(uint192 _newBorrowingFee) external override onlyOwner {
    if (_newBorrowingFee >= MAX_INIT_BORROWING_FEE) revert VaultController_FeeTooLarge();
    uint192 _oldBorrowingFee = initialBorrowingFee;
    initialBorrowingFee = _newBorrowingFee;

    emit ChangedInitialBorrowingFee(_oldBorrowingFee, _newBorrowingFee);
    }

    /// @notice Change the liquidation fee
     /// @param _newLiquidationFee The new liquidation fee
    function changeLiquidationFee(uint192 _newLiquidationFee) external override onlyOwner {
    if (_newLiquidationFee >= 1e18) revert VaultController_FeeTooLarge();
    uint192 _oldLiquidationFee = liquidationFee;
    liquidationFee = _newLiquidationFee;

    emit ChangedLiquidationFee(_oldLiquidationFee, _newLiquidationFee);
    }

    function changeProtocolFee(uint192 _newProtocolFee) external override onlyOwner {
    if (_newProtocolFee >= 1e18) revert VaultController_FeeTooLarge();
    protocolFee = _newProtocolFee;
    emit NewProtocolFee(_newProtocolFee);
    }
  


and this s where they are used:

     
    function liquidateVault(
    uint96 _id,
    address _assetAddress,
    uint256 _tokensToLiquidate
    ) external override paysInterest whenNotPaused returns (uint256 _toLiquidate) {
    ..

    uint192 _liquidationFee = getLiquidationFee(uint192(_tokensToLiquidate), _assetAddress);

    // finally, deliver tokens to liquidator
    _vault.controllerTransfer(_assetAddress, _msgSender(), _tokensToLiquidate - _liquidationFee);
    // and the fee to the treasury
    _vault.con
Incorrect calculations of USDA <> WUSDA conversion rate
Audit: 2023-07-amphora-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/WUSDA.sol#L60 , https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/WUSDA.sol#L71 , https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/WUSDA.sol#L80 , https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/WUSDA.sol#L91 , https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/WUSDA.sol#L100 , https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/WUSDA.sol#L111 , https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/WUSDA.sol#L120 , https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/WUSDA.sol#L131 , https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/WUSDA.sol#L140 , https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/WUSDA.sol#L160 , https://github.com/code-423n4/2023-07-amphora/blob/main/core/solidity/contracts/core/WUSDA.sol#L171


# Vulnerability details

## Impact
`WUSDA` is a non-rebasing wrapper of `USDA`, which entitles holders to a fixed % of `USDA`'s total supply / market cap. In this way, `WUSDA` ensures to capture value from `USDA` rebases into a non-rebasing token.
The issue with `WUSDA` is that all of the calculations converting between the two tokens uses `USDA`'s `totalSupply`.
This implies that `WUSDA` is calculating the wrapping rate as if **all** of `USDA`'s supply were deposited in the contract and wrapped, which is clearly an incorrect assumption for the contract to make.
As a result, an increase/decrease of `USDA`'s supply will result in a variation of the conversion rate between `WUSDA` and `USDA`, independently of the minted/burned `USDA` being wrapped/unwrapped.
In the following PoC, I show how a malicious actor is able to frontrun a user's `USDA` mint by wrapping his `USDA` and later unwrapping it for more `USDA` than he had at the beginning.

## Proof of Concept
To run this PoC, add it to `core/solidity/test/e2e/WUSDA.t.sol` and run with `forge test --mt test_MuliuserWusdaUnwrap_POC -vvv`

```solidity
function test_MuliuserWusdaUnwrap_POC() external {
  // Andy mints 100 USDA
  _depositSUSD(andy, andySUSDBalance);
  uint256 _andyUSDABalance = usdaToken.balanceOf(andy);

  // Andy wraps his USDA
  vm.startPrank(andy);
  usdaToken.approve(address(wusda), type(uint256).max);
  wusda.deposit(_andyUSDABalance);
  vm.stopPrank();

  /* @note it's important for Andy to wrap his tokens before Bob
    * mints USDA, as this will change the conversion rate! 
  */

  // Bob mints 1000 USDA
  _depositSUSD(bob, bobSUSDBalance);
  uint256 _bobUSDABalance = usdaToken.balanceOf(bob);

  // Bob wraps his USDA
  vm.startPrank(bob);
  usdaToken.approve(address(wusda), type(uint256).max);
  wusda.deposit(_bobUSDABalance);
  vm.stopPrank();

  // Andy unwraps