xanoutas/solidity-security-findings huggingface dataset:
griefer can create maximum length time locks for other users with only dust
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/main/governance/contracts/veOLAS.sol#L411-L418


# Vulnerability details

## Description
`veOLAS` is the voting token for the OLAS protocol. It functions like the curve.fi voting token in that a user gets more votes the longer they lock their tokens.

A user can create a lock for themselves or have another user create a lock for them, using [`veOLAS::createLockFor`](https://github.com/code-423n4/2023-12-autonolas/blob/main/governance/contracts/veOLAS.sol#L411-L418):
```solidity
File: governance/contracts/veOLAS.sol

411:    function createLockFor(address account, uint256 amount, uint256 unlockTime) external {
412:        // Check if the account address is zero
413:        if (account == address(0)) {
414:            revert ZeroAddress();
415:        }
416:
417:        _createLockFor(account, amount, unlockTime);
418:    }
```

The issue is that the user creating the lock sets the unlock time. Hence a malicious user could create a lock for another account for max time using just `1 wei` of `OLAS`. Thus the victim would be stuck with a max time lock that they cannot decrease.

## Impact
A malicious user can grief by creating locks for other users for max time with just `1 wei`. Thus the user is stuck with a maximum time lock.

## Proof of Concept
Test that can be added to `governance/test/veOLAS.js`:
```javascript
        it("Create lock prevented by attacker", async function () {
            const owner = signers[0];
            const account = signers[1];
            const attacker = signers[2];

            await olas.connect(owner).transfer(account.address,oneOLABalance);
            await olas.connect(owner).transfer(attacker.address,1);
            
            await olas.connect(attacker).approve(ve.address, 1);

            const maxDuration = 4 * 365 * 86400; // max time

            // attacker locks 1 wei for max duration
            await ve.connect(attacker).createLockFor(account.address, 1, maxDuration);

            // Lock end is maximum time
            const lockEnd = await ve.lockedEnd(account.address);
            const blockNumber = await ethers.provider.getBlockNumber();
            const block = await ethers.provider.getBlock(blockNumber);
            expect(Math.floor((block.timestamp + maxDuration) / oneWeek) * oneWeek).to.equal(lockEnd);

            // Account balance is 1 wei
            const pv = await ve.getLastUserPoint(account.address);
            expect(pv.balance).to.equal(1);

            // account tries to create lock but fails since lock is already there
            const lockDuration = oneWeek; // 1 week from now
            await olas.connect(account).approve(ve.address, oneOLABalance);
            await expect(
                ve.connect(account).createLock(oneOLABalance, lockDuration)
            ).to.be.revertedWithCustomError(ve, "LockedValueNotZero");
        });
```

## Tools Used
Manual audit

## Recommended Mitigation Steps
Consid
User's / protocol would be unable to withdraw intended amounts from Solana lockbox
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
User's / protocol unable to withdraw intended amounts from the lockbox

## Proof of Concept
When withdrawing it is required to pass all the associated accounts correctly. Hence the `getLiquidityAmountsAndPositions` function is implemented to obtain the required data for a given withdrawal amount.
But the function is wrongly implemented and gives incorrect withdrawal amount for the last position

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L338-L379
```solidity
    function getLiquidityAmountsAndPositions(uint64 amount)
        external view returns (uint64[] positionAmounts, address[] positionAddresses, address[] positionPdaAtas)
    {
        
        ......

        uint64 liquiditySum = 0;

        for (uint32 i = firstAvailablePositionAccountIndex; i < numPositionAccounts; ++i) {

            .....

            liquiditySum += positionLiquidity;

            .....

            if (liquiditySum >= amount) {
                amountLeft = liquiditySum - amount;
                break;
            }
        }

        for (uint32 i = 0; i < numPositions; ++i) {
            
            ....

            positionAmounts[i] = mapPositionAccountLiquidity[positionAddresses[i]];
            
            ....
        }

        // @audit incorrect
        if (numPositions > 0 && amountLeft > 0) {
            positionAmounts[numPositions - 1] = amountLeft;
        }
    }
```

Instead of subtracting `amountLeft` from the final position, the positionAmount is set as amountLeft. This can cause the overall sum to be greater or less than the actual amount

### Example
1 Position with liquidity 150
User wants to withdraw 100

Function walkthrough:
After first loop block, liquiditySum == 150 hence amountLeft = 50
In the second loop block, positionAmounts[0] would be set to 50
This would make the totalWithdrawal amount to 50 while the actual intended withdrawal was 100

## Tools Used
Manual review

## Recommended Mitigation Steps
Subtract from the final position instead of setting
```solidity
        if (numPositions > 0 && amountLeft > 0) {
            positionAmounts[numPositions - 1] -= amountLeft;
        }
```


## Assessed type

Math
Admin may be able to bypass the Gnosis cross chain governance authorization in future
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/bridges/HomeMediator.sol#L105-L116


# Vulnerability details

## Impact
Admin may be able to bypass the Gnosis cross chain governance authorization in future.

## Proof of Concept
The actions of `HomeMediator` (the governor relayer of Gnosis chain) is supposed to follow governance proposals created in Ethereum. This is enforced by restricting `msgSender` to match the address of Ethereum Timelock.

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/bridges/HomeMediator.sol#L105-L116
```solidity
    function processMessageFromForeign(bytes memory data) external {
        
        ......

        address governor = foreignGovernor;
        address bridgeGovernor = IAMB(AMBContractProxyHome).messageSender();
        if (bridgeGovernor != governor) {
            revert ForeignGovernorOnly(bridgeGovernor, governor);
        }  
```    

But the check doesn't enforce the source chain id to match Ethereum's chain id. Since additional networks may be added in the future, it is recommended to check the source chain id of the mesaage [docs](https://docs.tokenbridge.net/amb-bridge/development-of-a-cross-chain-application/how-to-develop-xchain-apps-by-amb#receive-a-method-call-from-the-amb-bridge) 
This may allow the admin in future, to avoid this authorization check by messaging from other chains. 

## Tools Used
Manual review

## Recommended Mitigation Steps
Keep a source id check as recommended


## Assessed type

Access Control
Possible DOS when withdrawing liquidity from Solana Lockbox
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Possible DOS when withdrawing liquidity from Lockbox

## Proof of Concept
When withdrawing it is required to pass all the associated accounts in the [transaction](https://solanacookbook.com/core-concepts/transactions.html#deep-dive). But among these (position,pdaPositionAccount and positionMint) are dependent on the current modifiable-state of the account ie. if another withdrawal occurs, the required accounts to be passed to the function call might change resulting in a revert.

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L194-L214
```solidity
    @mutableAccount(pool)
    @account(tokenProgramId)
    @mutableAccount(position)
    @mutableAccount(userBridgedTokenAccount)
    @mutableAccount(pdaBridgedTokenAccount)
    @mutableAccount(userWallet)
    @mutableAccount(bridgedTokenMint)
    @mutableAccount(pdaPositionAccount)
    @mutableAccount(userTokenAccountA)
    @mutableAccount(userTokenAccountB)
    @mutableAccount(tokenVaultA)
    @mutableAccount(tokenVaultB)
    @mutableAccount(tickArrayLower)
    @mutableAccount(tickArrayUpper)
    @mutableAccount(positionMint)
    @signer(sig)
    function withdraw(uint64 amount) external {
        address positionAddress = positionAccounts[firstAvailablePositionAccountIndex];
        if (positionAddress != tx.accounts.position.key) {
            revert("Wrong liquidity token account");
        }
```

The DOS for a withdrawal can be caused by another user withdrawing before the user's transaction. Due to the possibility to steal fees, attackers would be motivated to frequently call the withdraw method making such a scenario likely.   

## Tools Used
Manual review

## Recommended Mitigation Steps
To mitigate this it would require a redesign on how the lockbox accepts liquidity. Instead of adding new positions, the lockbox can keep its liquidity in a single position continuously increasing its liquidity for deposits. 


## Assessed type

Context
Solana Lockbox LP token fees can be stolen by an attacker
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Lockbox LP token fees can be stolen by an attacker

## Proof of Concept
If the entire liquidity associated with a position is withdrawn, the fees associated with the position is given to the user who has made the withdrawal. 

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L285-L307
```solidity
    function withdraw(uint64 amount) external {

        .....

        uint64 remainder = positionLiquidity - amount;
        mapPositionAccountLiquidity[positionAddress] = remainder;

        .....

        if (remainder == 0) {
            
            ....

            whirlpool.updateFeesAndRewards{accounts: metasUpdateFees, seeds: [[pdaProgramSeed, pdaBump]]}();

            AccountMeta[9] metasCollectFees = [
                AccountMeta({pubkey: pool, is_writable: true, is_signer: false}),
                AccountMeta({pubkey: pdaProgram, is_writable: false, is_signer: true}),
                AccountMeta({pubkey: positionAddress, is_writable: true, is_signer: false}),
                AccountMeta({pubkey: pdaPositionAta, is_writable: false, is_signer: false}),
                AccountMeta({pubkey: tx.accounts.userTokenAccountA.key, is_writable: true, is_signer: false}),
                AccountMeta({pubkey: tx.accounts.tokenVaultA.key, is_writable: true, is_signer: false}),
                AccountMeta({pubkey: tx.accounts.userTokenAccountB.key, is_writable: true, is_signer: false}),
                AccountMeta({pubkey: tx.accounts.tokenVaultB.key, is_writable: true, is_signer: false}),
                AccountMeta({pubkey: SplToken.tokenProgramId, is_writable: false, is_signer: false})
            ];
            whirlpool.collectFees{accounts: metasCollectFees, seeds: [[pdaProgramSeed, pdaBump]]}();
```

An attacker can exploit this to steal all the fees by making a deposit equal to the entire locked amount and the withdrawing the same amount. This will result in all the fees that has been accrued till now to go to the attacker.

## Tools Used
Manual review

## Recommended Mitigation Steps
The fees can be directed to the protocol's token accounts instead of the withdrawer.


## Assessed type

Access Control
Usage of GovernorCompatibilityBravo v4.8.3 allows for blocking proposals by frontrunning
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Governor proposal creation may be blocked by frontrunning

## Proof of Concept
GovernorOLAS inherits Openzeppelin's GovernorCompatibilityBravo v4.8.3.

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/GovernorOLAS.sol#L12-L15
```solidity
/// @title Governor OLAS - Smart contract for Autonolas governance
/// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz>
/// @dev The OpenZeppelin functions are used as is, version 4.8.3.
contract GovernorOLAS is Governor, GovernorSettings, GovernorCompatibilityBravo, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl {
```
In this version of `GovernorCompatibilityBravo`, an user is able to deny creation of any proposal by front-running, creating and cancelling the same proposal themselves. This will cause the original proposal creation tx to revert. This effectively gives any user the power to block all proposal creations[see](https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-5h3x-9wvq-w4m2)  

## Tools Used
Manual review

## Recommended Mitigation Steps
Use updated versions of OZ


## Assessed type

Access Control
Depository.deposit lacks slippage protection and deadline check
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
`Depository.deposit` lacks slippage protection and deadline check

## Proof of Concept
[Depository.deposit](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Depository.sol#L290-L346) can be used as deposit some LP token into the `Treasury` contract, and retrieve OLAS token by calling [Depository.redeem](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Depository.sol#L355-L391)
When [calculating](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Depository.sol#L320) the `payout` amount of OLAS, there are some factors can affect the [amountOLAS](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/GenericBondCalculator.sol#L46-L65)
1. `priceLP`
1. `ITokenomics.getLastIDF`

```
    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;
    }
```

As a tx can be pending in mempool for a long time, in a case that when a pending tx is executed, at which time the `priceLP` for LP token VS OLAS has changed a lot or IDF has changed, the `payout` can be quite different

## Tools Used
VIM

## Recommended Mitigation Steps
1. add deadline
1. add sl
Wrong assignement leads to the last position amount in `positionAmounts` being corrupted
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact

In [liquidity_lockbox, function getLiquidityAmountsAndPositions](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L338) will return a corrupted value under certain situations, making users withdraw the wrong amount of tokens.

## Proof of Concept

The purpose of [liquidity_lockbox, function getLiquidityAmountsAndPositions](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L338) is:

```solidity
    /// @dev Gets liquidity amounts and position accounts in order to correctly withdraw a specified liquidity amount.
```

As it is not used in the whole project, then this will be used by the front-end to query the needed arguments to pass to [withdraw](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L194C1-L210C48) in order to withdraw the amount specified by the user. The high-level workflow is as follows:

1. Check if the specified amount is higher than the total liquidity
2. Loop through all positions until the cummulative liquidity is equal or higher than the amount
   1. **NOTE** $\Rightarrow$ If the cummulative liquidity is **HIGHER**, then store the difference
3. Populate the return values
4. If `2.i` was triggered, update the last `positionAmounts` with such a value

As we can see, step `4` is flawed as the stored amount is **HIGHER** than the expected one:

```solidity
    function getLiquidityAmountsAndPositions(uint64 amount)
        external view returns (uint64[] positionAmounts, address[] positionAddresses, address[] positionPdaAtas)
    {
        // @audit step 1
        if (amount > totalLiquidity) {
            revert ("Requested amount is too big for the total available liquidity");
        }

        uint64 liquiditySum = 0;
        uint32 numPositions = 0;
        uint64 amountLeft = 0;

        // @audit step 2
        // Get the number of allocated positions
        for (uint32 i = firstAvailablePositionAccountIndex; i < numPositionAccounts; ++i) {
            address positionAddress = positionAccounts[i];
            uint64 positionLiquidity = mapPositionAccountLiquidity[positionAddress];

            // Increase a total calculated liquidity and a number of positions to return
            liquiditySum += positionLiquidity;
            numPositions++;

            // @audit step 2.1
            // Check if the accumulated liquidity is enough to cover the requested amount
            if (liquiditySum >= amount) {
                amountLeft = liquiditySum - amount;
                break;
            }
        }

        // @audit step 3
        // Allocate the necessary
Double updating accrued fees and rewards when closing positions
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact

In [liquidity_lockbox, function withdraw](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L210) will update two times the accrued fees and rewards when closing a position within the same transaction, leading to a no-op in the second one due to both timestamps being the same and Orca rejecting such an update.

## Proof of Concept

If we go to

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

```solidity
    function withdraw(uint64 amount) external {
        
        ...

        whirlpool.decreaseLiquidity{accounts: metasDecreaseLiquidity, seeds: [[pdaProgramSeed, pdaBump]]}(amount, 0, 0);

        ...

        // If requested amount can be fully covered by the current position liquidity, close the position
        if (remainder == 0) {
            // Update fees for the position
            AccountMeta[4] metasUpdateFees = [
                AccountMeta({pubkey: pool, is_writable: true, is_signer: false}),
                AccountMeta({pubkey: positionAddress, is_writable: true, is_signer: false}),
                AccountMeta({pubkey: tx.accounts.tickArrayLower.key, is_writable: false, is_signer: false}),
                AccountMeta({pubkey: tx.accounts.tickArrayUpper.key, is_writable: false, is_signer: false})
            ];
            whirlpool.updateFeesAndRewards{accounts: metasUpdateFees, seeds: [[pdaProgramSeed, pdaBump]]}();

            ...
        }
    }
```

we see that, when a position is gonna be closed after withdrawing all the remaining liquidity, the high level workflow will be:

1. Pull tokens
2. Burn them
3. Decrease liquidity
4. Update fees and rewards
5. Collect fees
6. Close position

However, if we go to the source code of Orca which handles step $3$ and $4$:

[**decrease_liquidity handler**](https://github.com/orca-so/whirlpools/blob/3206c9cdfbf27c73c30cbcf5b6df2929cbf87618/programs/whirlpool/src/instructions/decrease_liquidity.rs#L43C1-L50C8)

```rs
pub fn handler(
    ctx: Context<ModifyLiquidity>,
    liquidity_amount: u128,
    token_min_a: u64,
    token_min_b: u64,
) -> Result<()> {
    
    ...

    let clock = Clock::get()?;

    ...

    let timestamp = to_timestamp_u64(clock.unix_timestamp)?;

    ...

    sync_modify_liquidity_values(
        &mut ctx.accounts.whirlpool,
        &mut ctx.accounts.position,
        &ctx.accounts.tick_array_lower,
        &ctx.accounts.tick_array_upper,
        update,
        timestamp,
    )?;

    ...
}
```

[**liquidity_manager, function sync_modify_liquidity_values**](https://github.com/orca-so/whirlpools/blob/3206c9cdfbf27c73c30cbcf5b6df2929cbf87