xanoutas/solidity-security-findings huggingface dataset:
Absence of Oracle verification in `VaultController#registerErc20` and `VaultController#updateRegisteredErc20` functions
Audit: 2023-07-amphora-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Vulnerability Details

The `registerErc20` and `updateRegisteredErc20` functions do not include a check to ensure that the `_oracleAddress` provided is the intended Oracle for the token being registered or updated.

## Impact

Setting an incorrect Oracle for a token, whether newly registered or updated, can lead to token overvaluation/undervaluation. 

This situation could allow any user to increase their borrowing power or liquidate other users with undervalued vaults. 

The resulting fallout could lead to significant losses for the protocol or individual users.

## Proof of Concept

Consider the following scenario:

1. An owner executes an `updateRegisteredErc20()` operation to refresh `WETH` information with updated collateral data. Unfortunately, the `_oracleAddress` used pertains to the `WBTC` oracle.
2. A malicious user leverages a flash loan of `WETH`. They establish a new vault, deposit `WETH` into it exploiting the incorrect pricing, and take out a loan in `sUSD`, thereby draining all USDA reserves. The malicious user then repays the flash loan, harvesting the profits.

## Recommended Mitigation Steps

Ensure that `IOracleRelay(_oracleAddress).underlying()` matches `_tokenAddresses` within the `registerErc20` and `updateRegisteredErc20` functions.

```diff
function registerErc20(
    address _tokenAddress,
    uint256 _ltv,
    address _oracleAddress,
    uint256 _liquidationIncentive,
    uint256 _cap,
    uint256 _poolId
) external override onlyOwner {
+   if (IOracleRelay(_oracleAddress).underlying() != _tokenAddress) revert VaultController_IncorrectOracle();
```

```diff
function updateRegisteredErc20(
    address _tokenAddress,
    uint256 _ltv,
    address _oracleAddress,
    uint256 _liquidationIncentive,
    uint256 _cap,
    uint256 _poolId
) external override onlyOwner {
+   if (IOracleRelay(_oracleAddress).underlying() != _tokenAddress) revert VaultController_IncorrectOracle();
```


## Assessed type

Invalid Validation
The `VaultController` has the ability to burn the tokens even when the functions are paused by owners.
Audit: 2023-07-amphora-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact

The `VaultController` has the ability to burn the tokens even when the functions are paused by owners.

## Proof of Concept

It is possible for VaultController to burn the tokens (maybe acciddently), and break the protocol's intended functionality.

The `whenNotPaused` modifier is added to the `vaultControllerMint()` as well as the `vaultControllerTransfer()` functions which clearly states that `VaultController` should not be able to call these functions when the protocol is paused.

```solidity

function vaultControllerMint(address _target, uint256 _amount) external override onlyVaultController whenNotPaused {
    ...
  }

function vaultControllerTransfer(
    address _target,
    uint256 _susdAmount
  ) external override onlyVaultController whenNotPaused {
    ...
  }

```

But `vaultControllerBurn` does not have the modifier and thus should be noted by devs and sponsors.

```solidity
function vaultControllerBurn(address _target, uint256 _amount) external override onlyVaultController {
	if (_gonBalances[_target] < (_amount * _gonsPerFragment)) revert USDA_NotEnoughBalance();
	_burn(_target, _amount);
}
```

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

## Tools Used

Manual

## Recommended Mitigation Steps

Include the modifier to prevent burning tokens with protocol is paused.

```solidity
  function vaultControllerBurn(address _target, uint256 _amount) external override onlyVaultController whenNotPaused {
    if (_gonBalances[_target] < (_amount * _gonsPerFragment)) revert USDA_NotEnoughBalance();
    _burn(_target, _amount);
  }
```


## Assessed type

Access Control
`VaultController.liquidatVault` cannot make the vault solvent
Audit: 2023-07-amphora-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-amphora/blob/5d1cea9410db5448760c834f001af04a72edf3e0/core/solidity/contracts/core/VaultController.sol#L712
https://github.com/code-423n4/2023-07-amphora/blob/5d1cea9410db5448760c834f001af04a72edf3e0/core/solidity/contracts/core/VaultController.sol#L658
https://github.com/code-423n4/2023-07-amphora/blob/5d1cea9410db5448760c834f001af04a72edf3e0/core/solidity/contracts/core/VaultController.sol#L691


# Vulnerability details

## Impact
After liquidation, the vault is still not solvent. `VaultController.checkVault`, therefore, still returns `false`.
To summarize, the current problem is as follows.
1. uint256 liquidationAmount = VaultController.tokensToLiquidate(vaultId)
2. VaultController.liquidateVault(vaultId, asset, liquidationAmount)
3. `VaultController.checkVault(vaultId)` returns false

## Proof of Concept
Add this two lines at the end of [E2EVaultController.testOverLiquidationAndLiquidateInsolventVault](https://github.com/code-423n4/2023-07-amphora/blob/5d1cea9410db5448760c834f001af04a72edf3e0/core/solidity/test/e2e/VaultController.t.sol#L527).
Then run `forge test --mt testOverLiquidationAndLiquidateInsolventVault`.
The test liquidates a vault to the max, but it is still undercollateralized.
```solidity
// @audit 
bool overCollateralized = vaultController.checkVault(carolsVaultId);
assertFalse(overCollateralized);
```

## Tools Used
Manual review

## Recommended Mitigation Steps
I suggest two steps to mitigate this issue.

1. `VaultController.tokensToLiquidate` should return the amount of collateral asset to make the vault solvent.
However, it should not too much to increase the borrower's loss excessively. 
Other lending protocols provide good examples.

> In a liquidation, up to 50% of a borrower's debt is repaid and that value + liquidation fee is taken from the collateral available, so after a liquidation that amount liquidated from your debt is repaid.
Reference: https://docs.aave.com/faq/liquidations

2. The line below should be removed to make the vault solvent after liquidation. Other methods like `nonReentrant` lock or Check-Effect-Interaction pattern can be used for reentrancy protection.
```diff
    // this mainly prevents reentrancy
    - if (_getVaultBorrowingPower(_vault) > _vaultLiability(_id)) revert VaultController_OverLiquidation();
```
https://github.com/code-423n4/2023-07-amphora/blob/5d1cea9410db5448760c834f001af04a72edf3e0/core/solidity/contracts/core/VaultController.sol#L691


## Assessed type

Other
The `_totalSupply` of USDA exceeding `MAX_SUPPLY` would result in lowering the value of USDA token. This is also not the expected functionality.
Audit: 2023-07-amphora-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact

The `_totalSupply` excedding `MAX_SUPPLY` would result in bad accounting and lowering the value of USDA token.

## Proof of Concept

The `USDA.mint()` function calls `_mint(_msgSender(), _susdAmount);`

```solidity
  function mint(uint256 _susdAmount) external override paysInterest onlyOwner {
    if (_susdAmount == 0) revert USDA_ZeroAmount();
    _mint(_msgSender(), _susdAmount);
  }
```

Going further, it calls `_mint()` which increases the `_totalSupply` by `_amount` which is passed as a param.

```solidity
  /// @dev mint a specific `amount` of tokens to the `target`
  function _mint(address _target, uint256 _amount) internal {
    uint256 __gonsPerFragment = _gonsPerFragment;
    // the gonbalances of the sender is in gons, therefore we must multiply the deposit amount, which is in fragments, by gonsperfragment
    _gonBalances[_target] += _amount * __gonsPerFragment;
    // total supply is in fragments, and so we add amount
    _totalSupply += _amount;
    // and totalgons of course is in gons, and so we multiply amount by gonsperfragment to get the amount of gons we must add to totalGons
    _totalGons += _amount * __gonsPerFragment;
    // emit both a mint and transfer event
    emit Transfer(address(0), _target, _amount);
    emit Mint(_target, _amount);
  }
```

The issue is, before increasing the supply, it fails to check if the `_totalSupply` value exceeds the MAX_SUPPLY value.

The correct way to procees would be to continue only if:

`_totalSupply + _amount <= MAX_SUPPLY` otherwise, the function should revert.

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

## Tools Used

Manual

## Recommended Mitigation Steps

Add such require statment:

```solidity
require(_totalSupply + _amount <= MAX_SUPPLY, "Amount exceeds max supply")
```


## Assessed type

Other
`addRewardToken()` does note remove old entries before adding new ones
Audit: 2022-01-dev-test-repo-findings
Severity: HIGH

### Lines of code

--------------

[455](https://github.com/Tapioca-DAO/tap-token-audit/blob/59749be5bc2286f0bdbf59d7ddc258ddafd49a9f/contracts/governance/twTAP.sol#L455-L460), [280](https://github.com/Tapioca-DAO/tap-token-audit/blob/59749be5bc2286f0bdbf59d7ddc258ddafd49a9f/contracts/options/TapiocaOptionLiquidityProvision.sol#L280-L293), [378](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/Penrose.sol#L378-L388), [411](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/Penrose.sol#L411-L421)

### Vulnerability details

-------------

Each time `addRewardToken()` is called, new entries are added to the array, but doing so does not remove any old entries. By calling the function multiple times, an attacker can can increase their voting power indefinitely, without having to acquire new tokens.

```solidity
File: contracts/governance/twTAP.sol

455      function addRewardToken(IERC20 token) external onlyOwner returns (uint256) {
456          uint256 i = rewardTokens.length;
457          rewardTokens.push(token);
458          rewardTokenIndex[token] = i;
459          return i;
460:     }

```



```solidity
File: contracts/options/TapiocaOptionLiquidityProvision.sol

280      ) external onlyOwner updateTotalSGLPoolWeights {
281          require(assetID > 0, "tOLP: invalid asset ID");
282          require(
283              activeSingularities[singularity].sglAssetID == 0,
284              "tOLP: already registered"
285          );
286  
287          activeSingularities[singularity].sglAssetID = assetID;
288          activeSingularities[singularity].poolWeight = weight > 0 ? weight : 1;
289          sglAssetIDToAddress[assetID] = singularity;
290          singularities.push(assetID);
291  
292          emit RegisterSingularity(address(singularity), assetID);
293:     }

```



```solidity
File: contracts/Penrose.sol

378      /// @notice Registers an existing Singularity market (without deployment)
379      /// @dev can only be called by the owner
380      /// @param mc The address of the master contract which must be already registered
381      function addSingularity(
382          address mc,
383          address _contract
384      ) external onlyOwner registeredSingularityMasterContract(mc) {
385          isMarketRegistered[_contract] = true;
386          clonesOf[mc].push(_contract);
387          emit RegisterSingularity(_contract, mc);
388:     }

411      /// @notice Registers an existing BigBang market (without deployment)
412      /// @dev can only be called by the owner
413      /// @param mc The address of the master contract which must be already registered
414      function addBigBang(
415          address mc,
416          address _contract
417      ) external onlyOwner registeredBigBangMasterContract(mc) {
418          isMarketRegistered[_contract] = true;
419          clonesOf[mc].push(_contract);
420          emit RegisterBigB
Calls to `get_virtual_price()` are vulnerable to read-only reentrancy
Audit: 2022-01-dev-test-repo-findings
Severity: HIGH

### Lines of code

--------------

[117](https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/023751a4e987cf7c203ab25d3abba58f7344f213/contracts/oracle/implementations/ARBTriCryptoOracle.sol#L117-L142)

### Vulnerability details

-------------

`get_virtual_price()` was originally considered to be a manipulation-resistant price - suitable as a price oracle, but it was later found to be vulnerable to a [read-only reentrancy](https://chainsecurity.com/curve-lp-oracle-manipulation-post-mortem/) attack, where the Curve contract could be put into a partially-modified state, and an attacker could gain control via the raw external call `remove_liquidity()` makes. The attacker could use this to artificially inflate the price of the LP token/its balance, and use the inflated balance to take out loans which become undercollateralized at the end of the transaction, or to buy assets at exchange rates not actually available on the open market. In order to protect against the attack, many protocols call `uint256[2] calldata amts; ICurvePool(token).remove_liquidity(0, amts);` prior to calling `get_virtual_price()` since calling `remove_liquidity()` will ensure, via a reentrancy guard, that the user isn't currently manipulating the value, and since amounts are zero, it has no other effect. Another alternative is to call v1 [`pool.claim_admin_fees()`](https://etherscan.io/token/0x8301ae4fc9c624d1d396cbdaa1ed877821d7c511#code#L1104) or to call v2 `ICurveOwner(pool.owner()).withdraw_admin_fees(address(pool))`.

```solidity
File: contracts/oracle/implementations/ARBTriCryptoOracle.sol

117      function _get() internal view returns (uint256 _maxPrice) {
118          uint256 _vp = TRI_CRYPTO.get_virtual_price();
119  
120          // Get the prices from chainlink and add 10 decimals
121          uint256 _btcPrice = uint256(BTC_FEED.latestAnswer()) * 1e10;
122          uint256 _wbtcPrice = uint256(WBTC_FEED.latestAnswer()) * 1e10;
123          uint256 _ethPrice = uint256(ETH_FEED.latestAnswer()) * 1e10;
124          uint256 _usdtPrice = uint256(USDT_FEED.latestAnswer()) * 1e10;
125  
126          uint256 _minWbtcPrice = (_wbtcPrice < 1e18)
127              ? (_wbtcPrice * _btcPrice) / 1e18
128              : _btcPrice;
129  
130          uint256 _basePrices = (_minWbtcPrice * _ethPrice * _usdtPrice);
131  
132          _maxPrice = (3 * _vp * FixedPointMathLib.cbrt(_basePrices)) / 1 ether;
133  
134          // ((A/A0) * (gamma/gamma0)**2) ** (1/3)
135          uint256 _g = (TRI_CRYPTO.gamma() * 1 ether) / GAMMA0;
136          uint256 _a = (TRI_CRYPTO.A() * 1 ether) / A0;
137          uint256 _discount = Math.max((_g ** 2 / 1 ether) * _a, 1e34); // handle qbrt nonconvergence
138          // if discount is small, we take an upper bound
139          _discount = (FixedPointMathLib.sqrt(_discount) * DISCOUNT0) / 1 ether;
140  
141          _maxPrice -= (_maxPrice * _discount) / 1 ether;
142:     }

```


### Assessed type

------------

other
plsss
Audit: 2022-01-dev-test-repo-findings
Severity: HIGH

# Lines of code

#L12


# Vulnerability details

## Impact
Detailed description of the impact of this finding.

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

## Tools Used

## Recommended Mitigation Steps


## Assessed type

CanAuto
test
Audit: 2022-01-dev-test-repo-findings
Severity: HIGH

# Lines of code

#L14


# Vulnerability details

## Impact
Detailed description of the impact of this finding.

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

## Tools Used

## Recommended Mitigation Steps

lemme innnn


## Assessed type

Timing
An attacker can manipulate the preDepositvePrice to steal from other users.
Audit: 2022-01-dev-test-repo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L79
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L98


# Vulnerability details

## Impact

The first user that stakes can manipulate the total supply of sfTokens and by doing so create a rounding error for each subsequent user. In the worst case, an attacker can steal all the funds of the next user.

## Proof of Concept

When the first user enters totalSupply is set to 1e18 on [L79](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L79):
```solidity
if (totalSupply == 0)
            preDepositPrice = 10 ** 18; // initializes with a price of 1
        else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;
```
But the user can immediately unstake most of his safETH such that totalSupply <<  1e18. The attacker can then transfer increase the underlying amount by transferring derivative tokens to the derivative contracts. 

For subsequent users, the preDepositPrice will be heavily inflated and the calculation of mintAmount on [L98](https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L98):
```solidity
uint256 mintAmount = (totalStakeValueEth * 10 ** 18) / preDepositPrice;
```
can be very inaccurate. In the worst case it rounds down to 0 for users that deposit value that is less than the value that the attacker transferred in. 

In the following POC the attacker steals all of the second user's deposit. The attacker first deposits 100 ETH and immediately removes all but 1 wei. The attacker then transfers 10 wsETH to the WstEth contract. When the second user enters with 1.5 ETH no additional safETH are minted since the minAmount is rounded down to 0. The attacker has all of the safTokens and can withdraw 100% of deposits.


Create a new test file with the following content to run the POC.
```solidity
import { SafEth } from "../typechain-types";

import { ethers, upgrades, network } from "hardhat";
import { expect } from "chai";
import {
  getAdminAccount,
  getUserAccounts,
  getUserBalances,
  randomStakes,
  randomUnstakes,
} from "./helpers/integrationHelpers";
import { getLatestContract } from "./helpers/upgradeHelpers";
import { BigNumber } from "ethers";

import ERC20 from "@openzeppelin/contracts/build/contracts/ERC20.json";
import { RETH_MAX, WSTETH_ADRESS, WSTETH_WHALE } from "./helpers/constants";

describe.only("SafEth POC", function () {
  let safEthContractAddress: string;
  let strategyContractAddress: string;
  // create string array
  let derivativesAddress: string[] = [];
  let startingBalances: BigNumber[];
  let networkFeesPerAccount: BigNumber[];
  let totalStakedPerAccount: BigNumber[];

  before(async () => {
    startingBalances = await getUserBalances();
    networkFee
Calls to `get_virtual_price()` are vulnerable to read-only reentrancy
Audit: 2022-01-dev-test-repo-findings
Severity: HIGH

### Lines of code

--------------

[117](https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/023751a4e987cf7c203ab25d3abba58f7344f213/contracts/oracle/implementations/ARBTriCryptoOracle.sol#L117-L142)

### Vulnerability details

-------------

Impact

`get_virtual_price()` was originally considered to be a manipulation-resistant price - suitable as a price oracle, but it was later found to be vulnerable to a [read-only reentrancy](https://chainsecurity.com/curve-lp-oracle-manipulation-post-mortem/) attack, where the Curve contract could be put into a partially-modified state, and an attacker could gain control via the raw external call `remove_liquidity()` makes. The attacker could use this to artificially inflate the price of the LP token/its balance, and use the inflated balance to take out loans which become undercollateralized at the end of the transaction, or to buy assets at exchange rates not actually available on the open market. In order to protect against the attack, many protocols call `uint256[2] calldata amts; ICurvePool(token).remove_liquidity(0, amts);` prior to calling `get_virtual_price()` since calling `remove_liquidity()` will ensure, via a reentrancy guard, that the user isn't currently manipulating the value, and since amounts are zero, it has no other effect. Another alternative is to call v1 [`pool.claim_admin_fees()`](https://etherscan.io/token/0x8301ae4fc9c624d1d396cbdaa1ed877821d7c511#code#L1104) or to call v2 `ICurveOwner(pool.owner()).withdraw_admin_fees(address(pool))`.

```solidity
File: contracts/oracle/implementations/ARBTriCryptoOracle.sol

117      function _get() internal view returns (uint256 _maxPrice) {
118          uint256 _vp = TRI_CRYPTO.get_virtual_price();
119  
120          // Get the prices from chainlink and add 10 decimals
121          uint256 _btcPrice = uint256(BTC_FEED.latestAnswer()) * 1e10;
122          uint256 _wbtcPrice = uint256(WBTC_FEED.latestAnswer()) * 1e10;
123          uint256 _ethPrice = uint256(ETH_FEED.latestAnswer()) * 1e10;
124          uint256 _usdtPrice = uint256(USDT_FEED.latestAnswer()) * 1e10;
125  
126          uint256 _minWbtcPrice = (_wbtcPrice < 1e18)
127              ? (_wbtcPrice * _btcPrice) / 1e18
128              : _btcPrice;
129  
130          uint256 _basePrices = (_minWbtcPrice * _ethPrice * _usdtPrice);
131  
132          _maxPrice = (3 * _vp * FixedPointMathLib.cbrt(_basePrices)) / 1 ether;
133  
134          // ((A/A0) * (gamma/gamma0)**2) ** (1/3)
135          uint256 _g = (TRI_CRYPTO.gamma() * 1 ether) / GAMMA0;
136          uint256 _a = (TRI_CRYPTO.A() * 1 ether) / A0;
137          uint256 _discount = Math.max((_g ** 2 / 1 ether) * _a, 1e34); // handle qbrt nonconvergence
138          // if discount is small, we take an upper bound
139          _discount = (FixedPointMathLib.sqrt(_discount) * DISCOUNT0) / 1 ether;
140  
141          _maxPrice -= (_maxPrice * _discount) / 1 ether;
142:     }

```


### Assessed type

------------

other