xanoutas/solidity-security-findings huggingface dataset:
Changing one of the registry addresses in the Tokenomics contract may result in the loss of user rewards
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/main/tokenomics/contracts/Tokenomics.sol#L450-L469
https://github.com/code-423n4/2023-12-autonolas/blob/main/tokenomics/contracts/Tokenomics.sol#L1116-L1126


# Vulnerability details

## Impact
The owner of the component/agent may fail to claim his rewards if one of the registry addresses is changed during the lifetime of the protocol.

## Proof of Concept
The owner of the Tokenomics contract can change the registry addresses by calling `changeRegistries` function. These addresses are used in incentive accounting, where `componentRegistry` and agentRegistry` are basically NFT contracts proving ownership of the component/agent as well as storing dependencies information.

For example when user donates to one of the services, we get the components/agents ids from the service registry,

https://github.com/code-423n4/2023-12-autonolas/blob/main/tokenomics/contracts/Tokenomics.sol#L716-L717

Next we calculate reward fraction for each component/agent id that is part of this service

https://github.com/code-423n4/2023-12-autonolas/blob/main/tokenomics/contracts/Tokenomics.sol#L724-L755

```
                        if (incentiveFlags[unitType]) {
                            mapUnitIncentives[unitType][serviceUnitIds[j]].pendingRelativeReward += amount;
                        }
                        // If eligible, add relative top-up weights in the form of donation amounts.
                        // These weights will represent the fraction of top-ups for each component / agent relative
                        // to the overall amount of top-ups that must be allocated
                        if (topUpEligible && incentiveFlags[unitType + 2]) {
                            mapUnitIncentives[unitType][serviceUnitIds[j]].pendingRelativeTopUp += amount;
                            mapEpochTokenomics[curEpoch].unitPoints[unitType].sumUnitTopUpsOLAS += amount;
                        }
                    }
```

Once the epoch has passed owner of the component can claim this rewards for himself, to do this he can use the Dispencer contract that will forward the ids to the `accountOwnerIncentives` function, where we use `componentRegistry` and `agentRegistry` to validate ownership and correctness of the ids

https://github.com/code-423n4/2023-12-autonolas/blob/main/tokenomics/contracts/Dispenser.sol#L99
https://github.com/code-423n4/2023-12-autonolas/blob/main/tokenomics/contracts/Tokenomics.sol#L1103-L1127

```
        // Component / agent total supply
        uint256[] memory registriesSupply = new uint256[](2);
        for (uint256 i = 0; i < 2; ++i) {
            registriesSupply[i] = IToken(registries[i]).totalSupply();
        }

        // Check the input data
        uint256[] memory lastIds = new uint256[](2);
        for (uint256 i = 0; i < unitIds.length; ++i) {
            // Check for the unit type to be component / agent only
            if (unitTypes[i] > 1) {
Market attack against creation of bond products
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/GenericBondCalculator.sol#L70-L92


# Vulnerability details

## Impact

Upon creation of a new bond product, an attacker with enough funds could temporarily unbalance the Uniswap pool from which the price will be fetched, so that the newly created bond has a pumped `priceLP`. After that, the attacker could deposit a large amount of the product token into the newly created product in exchange of *"discounted"* OLAS, which would lead to a possible governance takeover scenario where the attacker had the majority of OLAS available (as the attacker would only need a $3\%$ of the total voting power, check [this](https://github.com/valory-xyz/autonolas-aip/blob/aip-3/content/aips/core-enhancing-autonolas-protocol-security.md#motivation)).

## Proof of Concept

It's a well known issue that using `getReserves` on an Uniswap pair with *"low"* liquidity to calculate the price of one of the tokens is dangerous as it can be manipulable via whales, flashloans and MEV bots. From the provided tests:

[**tokenomics/test/Depository.t.sol, line 120 and 121**](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/test/Depository.t.sol#L119C1-L121C70)

```solidity
    function testCreateProduct() public {
        
        ...

        uint256 priceLP = depository.getCurrentPriceLP(pair);
        depository.create(pair, priceLP, supplyProductOLAS, vesting);

        ...
    }
```

it can be seen that the usual workflow will be the following:

1. Fetch price by calling `Depository::GetCurrentPriceLP -> Uniswap::getReserves` and doing some maths.
2. Create the product with the derived `priceLP` from the previous step.

Unfortunately,

[**GenericBondCalculator, function getCurrentPriceLP**](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/GenericBondCalculator.sol#L70C1-L92C6)

```solidity
    function getCurrentPriceLP(address token) external view returns (uint256 priceLP)
    {
        IUniswapV2Pair pair = IUniswapV2Pair(token);
        uint256 totalSupply = pair.totalSupply();

        if (totalSupply > 0) {
            address token0 = pair.token0();
            address token1 = pair.token1();
            uint256 reserve0;
            uint256 reserve1;

            (reserve0, reserve1, ) = pair.getReserves(); // @audit red flag -> whales, flashloans -> manipulable

            if (token0 == olas || token1 == olas) {
                if (token0 == olas) {
                    reserve1 = reserve0;
                }

                priceLP = (reserve1 * 1e18) / totalSupply; // @audit no TWAP, no oracle, just raw price from reserves
            }
        }
    }
```

does calculate the exchange price by fetching the reserves of the Uniswap pair, which do reflect the exchange ratio at a **SINGLE** point of time, that is, even i
Withdrawals can be frozen by creating null deposits
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L84


# Vulnerability details

## Impact

It won't be possible to withdraw any LP token after doing a deposit of $0$ liquidity, leading to withdrawals being effectively freezed.

## Proof of Concept

In

[**liquidity_lockbox, function withdraw**](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L221C1-L225C10)

```solidity
        ...

        uint64 positionLiquidity = mapPositionAccountLiquidity[positionAddress];
        // Check that the token account exists
        if (positionLiquidity == 0) {
            revert("No liquidity on a provided token account");
        }

        ...
```

the code checks for the existence of a position via the recorded liquidity. This is a clever idea, as querying a non-existant value from a mapping will return $0$. However, in `deposit`, due to a flawed input validation, it is possible to make positions with $0$ liquidity as the only check being done is for liquidity to not be higher than `type(uint64).max`:

[**liquidity_lockbox, function _getPositionData**](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L94C1-L97C10)

```solidity
        ...

        // Check that the liquidity is within uint64 bounds
        if (positionData.liquidity > type(uint64).max) {
            revert("Liquidity overflow");
        }

        ...
```

As it will pass the input validation inside `_getPositionData`, the only way for such a tx to revert is in the [transfer](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L164)/[mint](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L171), which are low-level calls with no checks for success, as stated in my report `Missing checks for failed calls to the token program will corrupt user's positions`.

Due to the reasons above, this deposit with $0$ liquidity will be treated as a valid one and will be stored inside the `mapPositionAccountLiquidity` and `positionAccounts` arrays. If we add the fact that withdrawals are done by looping **LINEARLY** through `positionAccounts`:

[**liquidity_lockbox, function withdraw**](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L192C1-L323C6)

```solidity
    function withdraw(uint64 amount) external {
        address positionAddress = positionAccounts[firstAvailablePositionAccountIndex]; // @audit linear loop
        
        ...

        uint64 positionLiquidity = mapPositionAccountLiquidity[positionAddress];
        // Check that the token account
The calculation of voting power is incorrect
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/veOLAS.sol#L402-L454


# Vulnerability details

## Impact
When users [lock OLAS](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/veOLAS.sol#L425-L454), they receive veOLAS that can be used in governance as voting power. The locking length determines the multiplier which boosts voting rights. Voting rights should decrease linearly over the locking period as per documentation, but they are not.

Suppose a user locks their OLAS for a week, their voting power should decrease linearly over time and reach 0 at the end of the lock period. However, the current implementation causes the voting power to decrease to 0 before the lock period ends. As a result, users are unable to use their voting power when they should be able to.

## Proof of Concept
POC:
veOLAS.js

```js
it("Test Voting Power", async function () {
      // Transfer 10 OLAS to signers[1]
      const owner = signers[1];
      await olas.transfer(owner.address, tenOLABalance);

      // Approve signers[0] and signers[1] for 1 OLAS by voting escrow
      await olas.approve(ve.address, oneOLABalance);
      await olas.connect(owner).approve(ve.address, tenOLABalance);

      // Define 1 week for the lock duration
      const lockDuration = oneWeek; // 1 week from now

      ethers.provider.send("evm_increaseTime", [86400 * 21]);
      ethers.provider.send("evm_mine");
      //console.log((await ethers.provider.getBlock("latest")).timestamp);

      // Balance should be zero before the lock
      expect(await ve.getVotes(owner.address)).to.equal(0);
      // Locking (1 week)
      await ve.createLock(oneOLABalance, lockDuration);
      await ve.connect(owner).createLock(tenOLABalance, lockDuration);

      const lockEnd = await ve.lockedEnd(owner.address);
      console.log(lockEnd);

      console.log(
        "Voting power after locking: ",
        await ve.getVotes(signers[0].address)
      );
      console.log(
        "Voting power after locking: ",
        await ve.getVotes(owner.address)
      );

      // After 3 Days

      ethers.provider.send("evm_increaseTime", [86400 * 3]);
      ethers.provider.send("evm_mine");

      console.log(
        "Voting power of signer1 After 3days: ",
        await ve.getVotes(owner.address)
      );
      console.log(
        "Voting power of signer0 After 3days",
        await ve.getVotes(signers[0].address)
      );
    });
```

## Tools Used
Manual Review
## Recommended Mitigation Steps


## Assessed type

Other
Risk of losing TopUps due to silent failure
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/main/governance/contracts/OLAS.sol#L75
https://github.com/code-423n4/2023-12-autonolas/blob/main/tokenomics/contracts/Dispenser.sol#L89
https://github.com/code-423n4/2023-12-autonolas/blob/main/tokenomics/contracts/Tokenomics.sol#L1085
https://github.com/code-423n4/2023-12-autonolas/blob/main/tokenomics/contracts/Treasury.sol#L387


# Vulnerability details

## Impact
The `claimOwnerIncentives()` function may lead users to potentially lose their deserved topUps due to a silent failure in the `mint()` function, constrained by inflation control.

## Proof of Concept
Within the `claimOwnerIncentives()` function, the `accountOwnerIncentives()` on Tokenomics.sol is invoked when users claim their incentives. This function aggregates total rewards and top-ups and subsequently resets their balances.
```solidity
        for (uint256 i = 0; i < unitIds.length; ++i) {
            // Get the last epoch number the incentives were accumulated for
            uint256 lastEpoch = mapUnitIncentives[unitTypes[i]][unitIds[i]].lastEpoch;
            // Finalize unit rewards and top-ups if there were pending ones from the previous epoch
            // The finalization is needed when the trackServiceDonations() function did not take care of it
            // since between last epoch the donations were received and this current epoch there were no more donations
            if (lastEpoch > 0 && lastEpoch < curEpoch) {
                _finalizeIncentivesForUnitId(lastEpoch, unitTypes[i], unitIds[i]);
                // Change the last epoch number
                mapUnitIncentives[unitTypes[i]][unitIds[i]].lastEpoch = 0;
            }

            // Accumulate total rewards and clear their balances
            reward += mapUnitIncentives[unitTypes[i]][unitIds[i]].reward;
            mapUnitIncentives[unitTypes[i]][unitIds[i]].reward = 0;
            // Accumulate total top-ups and clear their balances
            topUp += mapUnitIncentives[unitTypes[i]][unitIds[i]].topUp;
            mapUnitIncentives[unitTypes[i]][unitIds[i]].topUp = 0;
        }
```
The accumulated `reward` and `topUp` values from `accountOwnerIncentives()` trigger the `withdrawToAccount()` function in the treasury. This function manages ETH distribution and OLAS token minting.
```solidity
        if (accountTopUps > 0) {
            IOLAS(olas).mint(account, accountTopUps);
            success = true;
            emit Withdraw(olas, account, accountTopUps);
        }
```
The potential issue arises from the `mint()` function's ability to silently fail without triggering a revert. This occurs as OLAS verifies against `inflationControl()` and only executes minting when the function returns true.
```solidity
    function mint(address account, uint256 amount) external {
        // Access control
        if (msg.sender != minter) {
            revert ManagerOnly(msg.sender, minter);
        }

        // Check the inflation schedule and
The bond product can only support the purchase of OLAS with a precision of 18 tokens
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/GenericBondCalculator.sol#L64
https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Depository.sol#L176
https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Depository.sol#L291-L346


# Vulnerability details

## Impact
Failure to recognize this problem can result in the calculated amount of OLAS being small, which can result in a loss of assets for the user.

## Proof of Concept
As you can see from the [notes](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Depository.sol#L176): token LP token to be deposited for pairs like OLAS-DAI, OLAS-ETH, etc.
This means that OLAS-USDC is also a potential supported token pair.

As you can see from the function [calculatePayoutOLAS](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/GenericBondCalculator.sol#L46-L65), the precision of the tokens are considered to be 18. 
```solidity
    function calculatePayoutOLAS(uint256 tokenAmount, uint256 priceLP) external view
        returns (uint256 amountOLAS)
    {
        // The result is divided by additional 1e18, since it was multiplied by in the current LP price calculation
        // The resulting amountDF can not overflow by the following calculations: idf = 64 bits;
        // priceLP = 2 * r0/L * 10^18 = 2*r0*10^18/sqrt(r0*r1) ~= 61 + 96 - sqrt(96 * 112) ~= 53 bits (if LP is balanced)
        // or 2* r0/sqrt(r0) * 10^18 => 87 bits + 60 bits = 147 bits (if LP is unbalanced);
        // tokenAmount is of the order of sqrt(r0*r1) ~ 104 bits (if balanced) or sqrt(96) ~ 10 bits (if max unbalanced);
        // overall: 64 + 53 + 104 = 221 < 256 - regular case if LP is balanced, and 64 + 147 + 10 = 221 < 256 if unbalanced
        // mulDiv will correctly fit the total amount up to the value of max uint256, i.e., max of priceLP and max of tokenAmount,
        // however their multiplication can not be bigger than the max of uint192
        uint256 totalTokenValue = mulDiv(priceLP, tokenAmount, 1);
        // Check for the cumulative LP tokens value limit
        if (totalTokenValue > type(uint192).max) {
            revert Overflow(totalTokenValue, type(uint192).max);
        }
        // Amount with the discount factor is IDF * priceLP * tokenAmount / 1e36
        // At this point of time IDF is bound by the max of uint64, and totalTokenValue is no bigger than the max of uint192
 --->     amountOLAS = ITokenomics(tokenomics).getLastIDF() * totalTokenValue / 1e36; // The precision of priceLP and tokenAmount are both considered to be 18, but in the case of usdc, the precision of tokenAmount should be 6.
    }
```

We know that usdc is a stablecoin with a precision of only 6. If OLAS-USD
malicious user can pay less than min accepted eth for service for all service ids except the first one
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Treasury.sol#L265


# Vulnerability details

## Impact
malicious user can dodge min accepted eth check for all service ids except the first one .

## Proof of Concept
protocol check that msg.value  with min accepted eth , not with amount ,So malicious user can dodge those check by passing msg.value which >= min accepted eth and amount will be probably min accepted eth.
## Tools Used
manual view
## Recommended Mitigation Steps
check with min accepted eth with amount 


## Assessed type

Invalid Validation
Incorrect Inflation Calculation for Bonding in Tokenomics.sol#Checkpoint()
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/main/tokenomics/contracts/Tokenomics.sol#L1017


# Vulnerability details

## Problem
The `checkpoint()` function in the smart contract incorrectly calculates inflation for an epoch when crossing into a new year. The issue arises from using the updated `curInflationPerSecond` for two different purposes - first, to represent the current year's inflation rate, and later, after recalculating, to represent the new year's rate.

## Impact

Inflation and rewards are not distributed with respect to the whitepaper, there will be more top-ups distributed if a new epoch cross a year 

## Proof of Concept
Here is where the problem lies : 
```solidity
    function checkpoint() external returns (bool) {
        /////// ...... ///////

        uint256 inflationPerEpoch;
        uint256 curInflationPerSecond = inflationPerSecond;
        uint256 numYears = (block.timestamp - timeLaunch) / ONE_YEAR;
        if (numYears > currentYear) {
            uint256 yearEndTime = timeLaunch + numYears * ONE_YEAR;
            inflationPerEpoch = (yearEndTime - prevEpochTime) * curInflationPerSecond;
            curInflationPerSecond = getInflationForYear(numYears) / ONE_YEAR;
            inflationPerEpoch += (block.timestamp - yearEndTime) * curInflationPerSecond;
            inflationPerSecond = uint96(curInflationPerSecond);
            currentYear = uint8(numYears);
            tokenomicsParametersUpdated = tokenomicsParametersUpdated | 0x04;
        } else {
            inflationPerEpoch = curInflationPerSecond * diffNumSeconds;
        }

        tp.epochPoint.totalTopUpsOLAS = uint96(inflationPerEpoch);
        incentives[4] = (inflationPerEpoch * tp.epochPoint.maxBondFraction) / 100;

        uint256 curMaxBond = maxBond;

        if (incentives[4] > curMaxBond) {
            incentives[4] = effectiveBond + incentives[4] - curMaxBond;
            effectiveBond = uint96(incentives[4]);
        }

        tp.epochPoint.endTime = uint32(block.timestamp);

        numYears = (block.timestamp + curEpochLen - timeLaunch) / ONE_YEAR;
        if (numYears > currentYear) {
            uint256 yearEndTime = timeLaunch + numYears * ONE_YEAR;
            inflationPerEpoch = (yearEndTime - block.timestamp) * curInflationPerSecond; //E @audit curInflationPerSecond has already been updated
            curInflationPerSecond = getInflationForYear(numYears) / ONE_YEAR;
            inflationPerEpoch += (block.timestamp + curEpochLen - yearEndTime) * curInflationPerSecond;
            curMaxBond = (inflationPerEpoch * nextEpochPoint.epochPoint.maxBondFraction) / 100;
            maxBond = uint96(curMaxBond);
            tokenomicsParametersUpdated = 0;
        } else if (tokenomicsParametersUpdated > 0) {
            curMaxBond = (curEpochLen * curInflationPerSecond * nextEpochPoint.epochPoint.maxBondFraction) / 100;
            maxBond = uint96(curMaxBond);
            tokenomicsParametersUpdated = 0;
        }
BridgedERC20::mint and burn operates on different accounts.
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/bridges/BridgedERC20.sol#L48-L55
https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/bridges/BridgedERC20.sol#L59-L66


# Vulnerability details

## Impact
ERC20Bridge tokens are minted for a specific account, but are always burnt for owner account.

## Proof of Concept
minting is done for a specific account
```
   _mint(account, amount);
```

Burning is done on owner.  Since only owner can enter the burn flow, the tokens
are burnt for the owner account.
```
 if (msg.sender != owner) {
            revert OwnerOnly(msg.sender, owner);
        }

   _burn(msg.sender, amount);
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
The token should be burnt for the account they were issued for.


## Assessed type

ERC20