xanoutas/solidity-security-findings huggingface dataset:
Permanent DOS in `liquidity_lockbox` for under $10
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/main/lockbox-solana/solidity/liquidity_lockbox.sol#L54
https://github.com/code-423n4/2023-12-autonolas/blob/main/lockbox-solana/solidity/liquidity_lockbox.sol#L181-L184


# Vulnerability details

## Impact
The `liquidity_lockbox` contract in the `lockbox-solana` project is vulnerable to permanent DOS due to its storage limitations. The contract uses a Program Derived Address (PDA) as a data account, which is created with a maximum size limit of 10 KB. 

Every time the `deposit()` function is called, a new element is added to `positionAccounts`, `mapPositionAccountPdaAta`, and `mapPositionAccountLiquidity`, which decreases the available storage by `64 + 32 + 32 = 128` bits. This means that the contract will run out of space after at most `80000 / 128 = 625` deposits.

Once the storage limit is reached, no further deposits can be made, effectively causing a permanent DoS condition. This could be exploited by an attacker to block the contract's functionality at a very small cost.

## Proof of Concept
An attacker can cause a permanent DoS of the contract by calling `deposit()` with the minimum position size only 625 times. This will fill up the storage limit of the PDA, preventing any further deposits from being made.

Since neither the contract nor seemingly Orca's pool contracts impose a limitation on the minimum position size, this can be achieved at a very low cost of `625 * dust * transaction fees`:

<img width="400" alt="no min deposit in SOL/OLAS pool" src="https://github.com/code-423n4/org/assets/153658521/48ca4eb0-5a4f-4310-a9ac-7869afa17ae8">

## Tools Used
Manual review

## Recommended Mitigation Steps
The maximum size of a PDA is 10 **KiB** on creation, only slightly larger than the current allocated space of 10 KB. The Solana SDK does provide a method to resize a data account ([source](https://docs.rs/solana-sdk/latest/solana_sdk/account_info/struct.AccountInfo.html#method.realloc)), but this functionality isn't currently implemented in Solang ([source](https://github.com/hyperledger/solang/issues/1434)).

A potential solution to this issue is to use an externally created account as a data account, which can have a size limit of up to 10 MiB, as explained in this [StackExchange post](https://solana.stackexchange.com/a/46). 

Alternatively, free up space by [clearing](https://solang.readthedocs.io/en/latest/language/contract_storage.html#how-to-clear-contract-storage) the aforementioned variables in storage for withdrawn positions.

However, a more prudent security recommendation would be to leverage the Solana SDK directly, despite the potential need for contract reimplementation and the learning curve associated with Rust. The Solana SDK offers greater flexibility and is less likely to introduce unforeseen vulnerabilities. Solang, while a valuable tool, is still under active development and will usually lag behind the SDK, which could inadvertently introduce complexit
CM can exploit a pause in `GuardCM` to gain permanent unrestricted access
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/multisigs/GuardCM.sol#L427-L429
https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/multisigs/GuardCM.sol#L400


# Vulnerability details

## Impact
The [`GuardCM`](https://github.com/code-423n4/2023-12-autonolas/blob/main/governance/contracts/multisigs/GuardCM.sol) contract is designed to restrict the Community Multisig (CM) actions within the protocol to only specific contracts and methods. Under specific [circumstances](https://github.com/valory-xyz/autonolas-aip/blob/aip-3/content/aips/core-enhancing-autonolas-protocol-security.md#original-governance-structure-and-its-evolution), the protocol allows the guard to be paused, which temporarily pauses the guard and provides the CM with full access to the timelock.

However, a potential vulnerability in the current implementation allows the CM to exploit a pause in `GuardCM` to gain permanent unrestricted access. This is because self-calls are only disallowed when the contract is [not paused](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/multisigs/GuardCM.sol#L427-L429). If it is paused, `checkTransaction` simply [always passes](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/multisigs/GuardCM.sol#L400). This means that the CM can take advantage of an event that causes governance to pause the guard, call [`setGuard()`](https://github.com/safe-global/safe-contracts/blob/186a21a74b327f17fc41217a927dea7064f74604/contracts/base/GuardManager.sol#L34) on itself and remove the guard altogether. This would effectively give the CM permanent unrestricted access, bypassing the guard's restrictions.

The CM holds all privileged roles within the timelock, which is the protocol owner. This means that the CM can potentially gain permanent unrestricted control over the entire protocol. As such, this vulnerability represents a significant risk of privilege escalation and is classified as high severity.
## Proof of Concept
The following sequence of events could lead to the CM gaining unrestricted access:

1. An event occurs that causes the governance to pause the `GuardCM` contract.
2. While the `GuardCM` contract is paused, the CM executes `setGuard(0)` on itself.
3. The `setGuard()` function removes the guard from the CM.
4. The CM now has permanent unrestricted access, bypassing the guard's restrictions.

## Tools Used
Manual review

## Recommended Mitigation Steps
To mitigate this issue, it is recommended to implement checks for self-calls even when the contract is paused. This would prevent the CM from being able to call the `setGuard()` function on itself and remove the guard:
```diff
@@ -397,7 +397,10 @@ contract GuardCM {
         bytes memory,
         address
     ) external {
-
CM can `delegatecall` to any address and bypass all restrictions
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/multisigs/GuardCM.sol#L405-L407


# Vulnerability details

## Impact
The [`GuardCM`](https://github.com/code-423n4/2023-12-autonolas/blob/main/governance/contracts/multisigs/GuardCM.sol) contract is designed to restrict the Community Multisig (CM) actions within the protocol to only specific contracts and methods. This is achieved by implementing a [`checkTransaction()`](https://github.com/code-423n4/2023-12-autonolas/blob/main/governance/contracts/multisigs/GuardCM.sol#L387) method, which is invoked by the CM `GnosisSafe` [before every transaction](https://github.com/safe-global/safe-contracts/blob/186a21a74b327f17fc41217a927dea7064f74604/contracts/GnosisSafe.sol#L147-L167). When `GuardCM` is not paused, the implementation restricts calls to the `schedule()` and `scheduleBatch()` methods in the timelock to only specific targets and selectors, performs additional checks on calls forwarded to the L2s and blocks self-calls on the CM itself, which prevents it from unilaterally removing the guard:
```solidity
	if (to == owner) {
		// No delegatecall is allowed
		if (operation == Enum.Operation.DelegateCall) {
			revert NoDelegateCall();
		}

		// Data needs to have enough bytes at least to fit the selector
		if (data.length < SELECTOR_DATA_LENGTH) {
			revert IncorrectDataLength(data.length, SELECTOR_DATA_LENGTH);
		}

		// Get the function signature
		bytes4 functionSig = bytes4(data);
		// Check the schedule or scheduleBatch function authorized parameters
		// All other functions are not checked for
		if (functionSig == SCHEDULE || functionSig == SCHEDULE_BATCH) {
			// Data length is too short: need to have enough bytes for the schedule() function
			// with one selector extracted from the payload
			if (data.length < MIN_SCHEDULE_DATA_LENGTH) {
				revert IncorrectDataLength(data.length, MIN_SCHEDULE_DATA_LENGTH);
			}

			_verifySchedule(data, functionSig);
		}
	} else if (to == multisig) {
		// No self multisig call is allowed
		revert NoSelfCall();
	}
```

However, a critical oversight in the current implementation allows the CM to perform `delegatecall`s to any address but the timelock. As can be seen above, `DelegateCall` operations are only disallowed when the target is the timelock (represented by the `owner` variable). What this effectively means is that the CM cannot run any `Timelock` function in its own context, but it can `delegatecall` to any other contract and hence execute arbitrary code. This allows it to trivially bypass the guard by delegating to a contract that removes the guard variable from the CM's storage.

The CM holds all privileged roles within the timelock, which is in turn the protocol's owner. This means that the CM can potentially gain unrestricted control over the entire protocol. As such, this vulnerability represents a significant risk of privilege escalation and is classified as high
In `FxERC20RootTunnel`, there is no option to get bridged tokens back in case the transaction can not be executed on L2 
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/bridges/FxERC20RootTunnel.sol#L86-L107


# Vulnerability details

## Impact
If the transaction on L2 fails to be executed the tokens deposited on L1 will be lost since there is no way to recover them


## Proof of Concept
When calling ` FxERC20RootTunnel:: _withdraw` the users deposits a certain amount of tokens to the bridge that he wants to withdraw on L2. The tokens are been burned and an event is emitted that initiates the withdraw on L2. The problem arises if the withdrawal on L2 fails for any reason. If this happens there is no way to recover the tokens the user deposited into `FxERC20RootTunnel`

## Tools Used
Manual review


## Recommended Mitigation Steps
Consider tracking which withdrawal succeeded on L2 and allow failed withdrawal to be reclaimed by the user by minting the amount deposited `FxERC20RootTunnel` back to his wallet 



## Assessed type

Other
[H1] Custom upgrade functionality is dangerous
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

### Impact

​    Unsafe upgrade can break protocol



### Analysis of the vulnerability

 You are using a custom upgrade for the tokenomics contract (not following UUPS standard)

     function changeTokenomicsImplementation(address implementation) external {
        // Check for the contract ownership
        if (msg.sender != owner) {
            revert OwnerOnly(msg.sender, owner);
        }
        // Check for the zero address
        if (implementation == address(0)) {
            revert ZeroAddress();
        }
    
        // Store the implementation address under the designated storage slot
        assembly {
            sstore(PROXY_TOKENOMICS, implementation)
        }
        emit TokenomicsImplementationUpdated(implementation);
    }



This is a bad idea for several reasons 

      1.  	You missed `initializeTokenomics` inside `changeTokenomicsImplementation` that you need to update storage variables with the new variables in case you add then.
      2.   Owner is set by solidity compiler (slot 0, not complaining with EIP1967).   This could cause the update to break the owner .  Instead you should put the owner at `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` (obtained as `bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)`).  This ensures that you if you make an incorrect upgrade at least you will be able to go back.
      

   




## Recommendation



​    Implement a complete UUPS Standard including EIP1967



## Assessed type

Upgradable
A MALICIOUS USER CAN FRONT RUN AND EXECUTE THE `Tokenomics.initializeTokenomics` TRANSACTION TO BECOME THE OWNER OF THE `Tokenomics` CONTRACT
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

The `Tokenomics.initializeTokenomics` is an external function which can be called by anyone, Since there is no access control. Hence a malicious user can front-run the valid initialization transaction and become the owner of the `Tokenomics` contract. 

This is because the `Tokenomics` contract is expected to be initialized no later than one year from the launch of the OLAS token contract. And anyone can call the `initializeTokenomics` function and become the `owner` of the contract as long as the `owner` is not set at the time of the `initializeTokenomics` function call as shown below:

        // Check if the contract is already initialized
        if (owner != address(0)) {
            revert AlreadyInitialized();
        } 

Hence a malicious user can easily front-run and become the owner of the `Tokenomics` contract. The subsequent `Tokenomics.initializeTokenomics` transaction by the `autonolas` protocol will revert since the `owner` is already set by the time it is being executed.

The malicious user can pass in input parameters which passes the input validation checks in the `initializeTokenomics` function, for `input addresses` and the `_epochLen` values. As a result the owner of the `Tokenomics` contract can be set to a malicious address. This will allow the malicious user to control the `owner` controlled functions such as `Tokenomics.changeTokenomicsImplementation`, `Tokenomics.changeManagers`, `Tokenomics.changeRegistries`, `Tokenomics.changeDonatorBlacklist`, `Tokenomics.changeTokenomicsParameters` and `Tokenomics.changeIncentiveFractions`. Even if the `autonolas` protocol tries to redploy and initialize the `Tokenomics` contract, the malicious user can keep on front-running the valid `Tokenomics.initializeTokenomics` transaction and become the owner of the `Tokenomics` contract maliciously.

As a result the `Tokenomics` contract will not be able to be used in the `autonolas` protocol since the malicious owner set tokenomics parameters are wrong and should not be used for transactions. This breaks the proper execution of the `autonolas` protocol.

## Proof of Concept

```solidity
    function initializeTokenomics(
        address _olas,
        address _treasury,
        address _depository,
        address _dispenser,
        address _ve,
        uint256 _epochLen,
        address _componentRegistry,
        address _agentRegistry,
        address _serviceRegistry,
        address _donatorBlacklist
    ) external
    {
        // Check if the contract is already initialized
        if (owner != address(0)) {
            revert AlreadyInitialized();
        }
```

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

```solid
veOLAS.sol : PointVoting's slope and bias values are reset to zero when depositing for another account.
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Since the bias (vote weight) and slope are reset, the voting power of as user is nullified.

## Proof of Concept
`veOLAS.sol` is escrow based contract where the OLAs tokens are locked for certain amount of time in order to gain the voting power.
This voting power would be used in governance related activities. veOLAs is virtual representation of locked OLAs tokens.

User can create a lock by calling the function [createLock](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/veOLAS.sol#L402-L404). 

```solidity
    function createLock(uint256 amount, uint256 unlockTime) external {
        _createLockFor(msg.sender, amount, unlockTime);
    }
```

Later this lock can be updated (increase the time or increase the amount) by calling the following functions.

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

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

during the process, [depositfor](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/veOLAS.sol#L330-L336) function would be called. This function again calls the [_checkpoint](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/veOLAS.sol#L173-L178) where the check point related parameters are updated. some of them are slope, time, amount, bias. where bias would be used to get the vote weight of an user.
Here, there are no min amount for an user.

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

```solidity
        if (account != address(0)) {
            // Schedule the slope changes (slope is going down)
            // We subtract new_user_slope from [newLocked.endTime]
            // and add old_user_slope to [oldLocked.endTime]
            if (oldLocked.endTime > block.timestamp) {
                // oldDSlope was <something> - uOld.slope, so we cancel that
                oldDSlope += uOld.slope;
                if (newLocked.endTime == oldLocked.endTime) {
                    oldDSlope -= uNew.slope; // It was a new deposit, not extension
                }
                mapSlopeChanges[oldLocked.endTime] = oldDSlope;
            }

            if (newLocked.endTime > block.timestamp && newLocked.endTime > oldLocked.endTime) {
                newDSlope -= uNew.slope; // old slope disappeared at this point
                mapSlopeChanges[newL
tokens can be deposited and immediately withdrawn before the intended lock time by depositing right before expiry
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
tokens can be deposited and immediately withdrawn before the intended lock time by depositing right before expiry.

## Proof of Concept
There is  edge cases around the locking and unlocking periods that are not fully considered in the contract. Specifically, Deposits just before expiry:

The contract does not properly handle deposits made right before a lock expires

The code does not properly check or handle deposits made right before a lock expires. For example:

       if (lockedBalance.endTime < (block.timestamp + 1)) {
         revert LockExpired(msg.sender, lockedBalance.endTime, block.timestamp);
       }
This only checks if the lock has already expired. But if the lock is expiring in the next block, deposits could still be made. This means tokens may be deposited into an expired lock

 For example:

- Alice creates a 1 year lock for 100 tokens 
- After 364 days, Alice deposits another 50 tokens into the lock
- The next day, the initial 1 year lock expires

In this case, the additional 50 token deposit is exposed to the vulnerability of withdrawing before the lock time. The contract should not allow additional deposits right before expiry to prevent this.


## Tools Used
Manual
## Recommended Mitigation Steps
Addi a check in the depositFor and increaseAmount functions to prevent deposits within a certain window (e.g. 1 week) before the lock expiry:

       function depositFor(address account, uint256 amount) external {
         LockedBalance memory lockedBalance = mapLockedBalances[account];
  
         // Only allow deposit if lock expires > 1 week from now
         require(lockedBalance.endTime > (block.timestamp + 1 weeks), "Deposit 
       too close 



## Assessed type

Other
Mint function can  exceeds a predefined limitin when flationRemainder function exist overflow at OLAS.sol contract 
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/OLAS.sol#L113
https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/lib/solmate/src/tokens/ERC20.sol#L195-L206


# Vulnerability details

## Impact
mint function can  exceeds a predefined limit when inflationRemainder 
function exist overflow at OLAS.sol contract 

## Proof of Concept

1.First call Burn function,Burn function call _burn can cause overflow,
we can get a big totalSupply.
https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/lib/solmate/src/tokens/ERC20.sol#L195-L206

2.Second call mint. At inflationRemainder function, remainder = supplyCap - _totalSupply may be overflow when numYears = 0 because totalSupply  is uint256 (2^^256 -1 > tenYearSupplyCap == 1_000_000_000e18)                                                           https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/OLAS.sol#L113

    function inflationRemainder() public view returns (uint256 remainder) {
        uint256 _totalSupply = totalSupply;
        // Current year
        uint256 numYears = (block.timestamp - timeLaunch) / oneYear;
        // Calculate maximum mint amount to date
        uint256 supplyCap = tenYearSupplyCap;
        // After 10 years, adjust supplyCap according to the yearly inflation % set in maxMintCapFraction
        if (numYears > 9) {
            // Number of years after ten years have passed (including ongoing ones)
            numYears -= 9;
            for (uint256 i = 0; i < numYears; ++i) {
                supplyCap += (supplyCap * maxMintCapFraction) / 100;
            }
        }
        // Check for the requested mint overflow
        remainder = supplyCap - _totalSupply;     // bug overflow
    }

## Tools Used
vscode

## Recommended Mitigation Steps inflationRemainder 
1.fix _burn and _mint function at solmate  use safemath function
2.fix remainder = supplyCap - _totalSupply
remainder = supplyCap > _totalSupply ? supplyCap.sub(_totalSupply) : 0;



## Assessed type

Under/Overflow
Attacker can call initializeTokenomics in the tokenomics implementation contract and self destruct afterwards
Audit: 2023-12-autonolas-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Malicious actor can front run any attempts to initialize the implementation contract of tokenomics and self destruct the contract. This makes us to re-deploy proxy as the upgradable logic is within the proxy as well.

## Proof of Concept
1) Implementation is deployed
2) As soon as its deployed, or in the same block. Attacker sends init transaction to the tokenomics implementation contract
3) Proxy is deployed and initialized using implementation contract by deployer
4) Attacker self destructs implementation contract using any arbitrary address call


## Tools Used
manual analysis

## Recommended Mitigation Steps
have owner = msg.sender in constructor of tokenomics implementation contract, this renders it unusable while not affecting the proxy


## Assessed type

Access Control