xanoutas/solidity-security-findings huggingface dataset:
Users Can Exceed Purchase Limit
Audit: 2024-06-vultisig-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/main/src/ILOPool.sol#L151


# Vulnerability details

## Impact

Users who are part of the `ILOPool whitelist` can exceed their `saleInfo.maxCapPerUser` and transfer their NFTs to users without a whitelist, effectively allowing them to buy from others indefinitely.

## Proof of Concept

ILOPool.t.sol:

```
    function testBuyForOtherUser() external {
        _prepareBuy();
        (uint256 tokenId, uint128 liquidity) = _buy(SALE_START+1, 60000 ether);
        vm.expectRevert();
        _buy(SALE_START+1, 1);
        vm.startPrank(INVESTOR);
        IILOPool(iloPool).transferFrom(INVESTOR, makeAddr("user"), tokenId);
        vm.stopPrank();
        _buy(SALE_START+1, 1);
    }
```

## Tools Used

Foundry

## Recommended Mitigation Steps

Track Individual Purchases: Keep a record of each whitelist user's purchases to ensure they do not exceed their allowed cap.


## Assessed type

Other
Use of `slot0` to get `sqrtPriceLimitX96` can lead to price manipulation.
Audit: 2024-06-vultisig-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/src/ILOManager.sol#L189


# Vulnerability details

## Impact
The use of `slot0` to retrieve `sqrtPriceX96` in `src/ILOManager::launch` function can lead to price manipulation. A malicious actor could manipulate the Uniswap V3 pool's price just before calling the `launch` function, thereby meeting the requirement for `sqrtPriceX96`. This could result in launching the project at an incorrect price.

## Proof of Concept
In the `launch` function, the current price is fetched using `slot0`:
```
function launch(address uniV3PoolAddress) external override {
    require(block.timestamp > _cachedProject[uniV3PoolAddress].launchTime, "LT");
@>    (uint160 sqrtPriceX96, , , , , , ) = IUniswapV3Pool(uniV3PoolAddress).slot0();
@>    require(_cachedProject[uniV3PoolAddress].initialPoolPriceX96 == sqrtPriceX96, "UV3P");
    address[] memory initializedPools = _initializedILOPools[uniV3PoolAddress];
    require(initializedPools.length > 0, "NP");
    for (uint256 i = 0; i < initializedPools.length; i++) {
        IILOPool(initializedPools[i]).launch();
    }
    emit ProjectLaunch(uniV3PoolAddress);
}
```
A malicious actor can manipulate the price by executing large trades just before calling the `launch` function, making the `sqrtPriceX96` match the required initial pool price, thereby triggering the launch under manipulated conditions.

## Tools Used
Manual review

## Recommended Mitigation Steps
Use the `TWAP` function to get the value of `sqrtPriceX96`.


## Assessed type

Uniswap
Launch depends on the uniswap pool state to work. This function will fail if there's already circulating supply and people can trade
Audit: 2024-06-vultisig-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/main/src/ILOManager.sol#L190


# Vulnerability details

## Impact
Anyone (attacker or not) can stop the launch of the ILOPool if they trade on the Uniswap pool and change its price. If it is different from the ILOManager contract stored price for that pool, the launch function will always revert.

## Proof of Concept

If a project has already circulating supply on its token, anyone can add liquidity and trade on newly created pools. This will change the uniswapv3 pool state (the [price of the token](https://github.com/code-423n4/2024-06-vultisig/blob/main/src/ILOManager.sol#L189)) and avoid the launch of the ILOPool.

```solidity
    function launch(address uniV3PoolAddress) external override {
        require(block.timestamp > _cachedProject[uniV3PoolAddress].launchTime, "LT");
        (uint160 sqrtPriceX96, , , , , , ) = IUniswapV3Pool(uniV3PoolAddress).slot0();
        // HERE WILL ALWAYS REVERT IF THE POOL IS ALREADY BEING USED
        require(_cachedProject[uniV3PoolAddress].initialPoolPriceX96 == sqrtPriceX96, "UV3P");
        address[] memory initializedPools = _initializedILOPools[uniV3PoolAddress];
        require(initializedPools.length > 0, "NP");
        for (uint256 i = 0; i < initializedPools.length; i++) {
            IILOPool(initializedPools[i]).launch();
        }

        emit ProjectLaunch(uniV3PoolAddress);
    }
```

Example:

- project has an already circulating supply of its token and want to create another ILOPool with an existing or new uniswap pool.

- [code](https://github.com/code-423n4/2024-06-vultisig/blob/main/src/ILOManager.sol#L57): owner of project initializes uniswap pool ([if applicable](https://github.com/code-423n4/2024-06-vultisig/blob/main/src/ILOManager.sol#L61)) and project. ILOManager records uniswap [initial price](https://github.com/code-423n4/2024-06-vultisig/blob/main/src/ILOManager.sol#L142).

- [code](https://github.com/code-423n4/2024-06-vultisig/blob/main/src/ILOManager.sol#L72): owner initializes the ILOPool.

- [code](https://github.com/Uniswap/v3-core/blob/main/contracts/UniswapV3Pool.sol#L57-L58): Anyone makes any trade on the Uniswap pool, thus changing its price

- [code](https://github.com/code-423n4/2024-06-vultisig/blob/main/src/ILOManager.sol#L187): If price differs from ILOPool stored price on launch it will always [revert](https://github.com/code-423n4/2024-06-vultisig/blob/main/src/ILOManager.sol#L189-L190).

- Attacker can keep trading before the launch to avoid the uniswap pool price to match.

## Tools Used
Manual review

## Recommended Mitigation Steps

The launch should not depend or revert if the initial pool price is different. At least it should only revert if it's out of range, and range should be changeable by the admin of the project.





## Assessed type

DoS
Malicious user can honeypot NFT buyer by claiming all vested amounts before sale
Audit: 2024-06-vultisig-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/src/ILOPool.sol#L144
https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/src/ILOPool.sol#L184


# Vulnerability details

## Impact
`ILOPool` is an `ERC721` token that can be traded freely on the secondary markets (example Opensea). These NFTs are tied to vesting balances in the `ILOPool` contract. Since the owner of the nft can call the `ILOPool::claim()` to claim his vested amount at any moment (after launch), it exposes buyers of NFTs on secondary markets to the risk of acquiring devalued assets.

## Proof of Concept
Consider the following scenario :

- Alice mints an NFT tied to a vested position in the pool after calling `buy` function.
- Alice decides to sell the NFT on a secondary market like OpenSea after discussing the terms with a buyer.
- Prior to the sale, Alice claims the accrued vested tokens associated with the NFT, significantly reducing the value tied to the NFT.
- At the same time the buyer sends buying TX and receives the NFT with much less vested amounts than he expected, overpaying for the the asset.
## Tools Used

Manual review

## Recommended Mitigation Steps

Consider adding functionality that pauses claims or refunds when the NFT is for sale.


## Assessed type

ERC721
Non-whitelisted actors can obtain `ILOPool` NFT positions when they should not be allowed to
Audit: 2024-06-vultisig-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/58ebda57ccf6a74bdef2b88eb18a62ec4ad46112/src/ILOPool.sol#L126-L176


# Vulnerability details

## Impact
Calling the following `buy` function should only mint an `ILOPool` NFT position to a recipient, who is whitelisted. However, after receiving such `ILOPool` NFT position, such whitelisted recipient can transfer it to another suspicious actor, who is not whitelisted, since the `ILOPool` contract has no way to revert such transfer. In this case, the whitelist requirement, which attempts to prevent non-whitelisted actors from owning `ILOPool` NFT positions, is bypassed and ineffective. As a result, non-whitelisted actors can obtain `ILOPool` NFT positions when they should not be allowed to.

https://github.com/code-423n4/2024-06-vultisig/blob/58ebda57ccf6a74bdef2b88eb18a62ec4ad46112/src/ILOPool.sol#L126-L176
```solidity
    function buy(uint256 raiseAmount, address recipient)
        external override 
        returns (
            uint256 tokenId,
            uint128 liquidityDelta
        )
    {
        require(_isWhitelisted(recipient), "UA");
        ...
        if (balanceOf(recipient) == 0) {
            _mint(recipient, (tokenId = _nextId++));
            ...
        } else {
            ...
        }
        ...
        // transfer fund into contract
        TransferHelper.safeTransferFrom(RAISE_TOKEN, msg.sender, address(this), raiseAmount);
        ...
    }
```

## Proof of Concept
The following steps can occur for the described scenario.
1. Alice is whitelisted for obtaining an `ILOPool` NFT position.
2. Alice calls the `buy` function to mint an `ILOPool` NFT position to herself.
3. Alice transfers such `ILOPool` NFT position to Bob, who is not whitelisted.
4. Bob now owns such `ILOPool` NFT position when he should not be allowed to.

## Tools Used
Manual Review

## Recommended Mitigation Steps
The `ILOPool` contract can add a `_beforeTokenTransfer` function that overrides the `_beforeTokenTransfer` function of OpenZeppelin's `ERC721` contract and reverts if `to` is not whitelisted.


## Assessed type

Invalid Validation
using pool slot 0 price can cause issues
Audit: 2024-06-vultisig-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/src/ILOManager.sol#L189
https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/src/ILOManager.sol#L115


# Vulnerability details

## Impact
Because slot0 price is directy read from the pool, an attacker can manipulate the pool  to make the functions `_initUniV3PoolIfNecessary()` and `launch()` not work as intended. 

## Proof of Concept
 ### 1.) for `_initUniV3PoolIfNecessary()`
   the function `_initUniV3PoolIfNecessary()` uses uniV3pool.slot0 price to compare thre given sqrt price at pool initialization against the current sqrt price in the pool if the pool is already existing. see Below 

https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/src/ILOManager.sol#L109C1-L123C1
```solidity 
    function _initUniV3PoolIfNecessary(PoolAddress.PoolKey memory poolKey, uint160 sqrtPriceX96) internal returns (address pool) {
        pool = IUniswapV3Factory(UNIV3_FACTORY).getPool(poolKey.token0, poolKey.token1, poolKey.fee);
        if (pool == address(0)) {
            pool = IUniswapV3Factory(UNIV3_FACTORY).createPool(poolKey.token0, poolKey.token1, poolKey.fee);
            IUniswapV3Pool(pool).initialize(sqrtPriceX96);
        } else {
            (uint160 sqrtPriceX96Existing, , , , , , ) = IUniswapV3Pool(pool).slot0();
            if (sqrtPriceX96Existing == 0) {
                IUniswapV3Pool(pool).initialize(sqrtPriceX96);
            } else {
                require(sqrtPriceX96Existing == sqrtPriceX96, "UV3P");
            }
        }
    }
```

if pool is already existing, it will require the current `sqrtPriceX96Existing` in the pool to be same as the `sqrtPriceX96` given as parameter. 
`_initUniV3PoolIfNecessary()`is called in [initProject()](https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/src/ILOManager.sol#L61). An attacker can 
- watch the mempool for calls to `initProject()` 
 - extract the parameters to initProject() and generate the poolKey via `PoolAddress.getPoolKey()`
 - assert that the pool is existing and in a front runned tx manipulate its`sqrtPriceX96Existing` price to not be equal to the params.initialPoolPriceX96 value in the   `initProject()` parameters.
 - this then causes the call to `initProject()` which comes after to fail/revert. 


 ### 2.) for `launch()`
  the `launch(0` function compares the cached value `_cachedProject[uniV3PoolAddress].initialPoolPriceX96` against the current  sqrtPriceX96 price of the pool. Once again this can be front ran by an attacker to prevent launch of the project. 

https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/src/ILOManager.sol#L187C1-L198C6
```solidity
    function launch(address uniV3PoolAddress) external override {
        require(block.timestamp > _cachedProject[uniV3PoolAddress].launchTime, "LT");
        (uin
`claim` function lacks slippage controls for `amount0` and `amount1` returned by `pool.burn` function call
Audit: 2024-06-vultisig-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/58ebda57ccf6a74bdef2b88eb18a62ec4ad46112/src/ILOPool.sol#L184-L261
https://github.com/Uniswap/v3-periphery/blob/main/contracts/NonfungiblePositionManager.sol#L257-L306


# Vulnerability details

## Impact
Because the `claim` function does not have slippage controls for `amount0` and `amount1` returned by the `pool.burn` function call, the `claim` function call can suffer from price manipulation on the associated Uniswap v3 pool. If a price manipulation frontruns the `claim` transaction, the claimed token amounts can be much less than what they should be.

## Proof of Concept
When calling the following `claim` function, there are no slippage controls for `amount0` and `amount1` returned by the `pool.burn` function call. This is unlike Uniswap's `decreaseLiquidity` function below that does execute `require(amount0 >= params.amount0Min && amount1 >= params.amount1Min, 'Price slippage check')`, where `amount0` and `amount1` are also returned by the `pool.burn` function call. Thus, if a price manipulation on the associated Uniswap v3 pool frontruns the `claim` transaction, `amount0` and `amount1` can be much less than what they should be when the `claim` transaction is executed, which would cause the investor to claim token amounts that are much less than what they should be.

https://github.com/code-423n4/2024-06-vultisig/blob/58ebda57ccf6a74bdef2b88eb18a62ec4ad46112/src/ILOPool.sol#L184-L261
```solidity
    function claim(uint256 tokenId)
        external
        payable
        override
        isAuthorizedForToken(tokenId)
        returns (uint256 amount0, uint256 amount1)
    {
        // only can claim if the launch is successfully
        require(_launchSucceeded, "PNL");

        // calculate amount of unlocked liquidity for the position
        uint128 liquidity2Claim = _claimableLiquidity(tokenId);
        IUniswapV3Pool pool = IUniswapV3Pool(_cachedUniV3PoolAddress);
        Position storage position = _positions[tokenId];
        {
            IILOManager.Project memory _project = IILOManager(MANAGER).project(address(pool));

            uint128 positionLiquidity = position.liquidity;
            require(positionLiquidity >= liquidity2Claim);

            // get amount of token0 and token1 that pool will return for us
            (amount0, amount1) = pool.burn(TICK_LOWER, TICK_UPPER, liquidity2Claim);

            // get amount of token0 and token1 after deduct platform fee
            (amount0, amount1) = _deductFees(amount0, amount1, _project.platformFee);

            bytes32 positionKey = PositionKey.compute(address(this), TICK_LOWER, TICK_UPPER);

            // calculate amount of fees that position generated
            (, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = pool.positions(positionKey);
            uint256 fees0 = FullMath.mulDiv(
                                feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128,
Buffer Depletion Vulnerability in DelayBuffer Library
Audit: 2024-05-arbitrum-foundation-findings
Severity: HIGH

# Lines of code

https://github.com/AnasTur/2024-05-arbitrum-foundation/blob/fa413646bb4776c114234bcde97bab901f3653e0/src/bridge/DelayBuffer.sol#L33


# Vulnerability details

**Vulnerability:**

The `calcBuffer()` function has a potential vulnerability in the following scenario:

* The `threshold` is set to a high value, allowing for a significant delay in message processing.
* A malicious user submits a batch of messages with a delay greater than the threshold.
* The `calcBuffer()` function depletes the delay buffer by the amount of the delay, even though only a portion of the messages in the batch are actually delayed.

This allows the malicious user to force the sequencer to process a large number of messages without having to wait the full threshold time.

**Exploitation:**

1. Submit a batch of messages with a delay greater than the threshold.
2. Monitor the delay buffer and note the amount it is depleted.
3. Submit another batch of messages with the same delay.
4. The `calcBuffer()` function will again deplete the buffer by the amount of the delay, even though only a portion of the messages in the second batch are actually delayed.
5. Repeat steps 3 and 4 until the sequencer runs out of buffer space.

**Impact:**

* The sequencer may process messages that are not actually delayed, leading to incorrect ordering of transactions.
* This vulnerability can potentially allow a malicious user to bypass the delay buffer and force the sequencer to process messages at an arbitrary time.

**Mitigation:**

* Limit the threshold to a reasonable value.
* Use a mechanism to verify the actual delay of messages before depleting the buffer.
* Consider implementing additional safeguards to prevent malicious manipulation of the delay buffer.


## Assessed type

Timing
Wining a challenge by engineering time
Audit: 2024-05-arbitrum-foundation-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-arbitrum-foundation/blob/main/src/challengeV2/EdgeChallengeManager.sol#L511


# Vulnerability details

## Impact
If the difference between `challengePeriodBlocks` and the total unrivaled time of a malicious assertion claim is less than the delay between sending a transaction and mining that transaction, the malicious assertion claim will always win regardless of the total unrivaled time of the valid assertion claim.

## Proof of Concept

Please have a look at the following time demonstration for better understanding:

```
 ---- t1 ---- t1 + k ---- t1 + tH ---- t1 + tH + tA ---- t1 + k + tH ---- t1 + tH + tA + k ---- t1 + k + tH + k ----> t

    Honest    Honest       Evil         Evil               Honest               Evil                  Honest
    Create    Create       Rival &      Confirm            Confirm              Confirm               Confirm
                           Create
    Send      Mined        Send         Send               Send                 Mined                 Mined

                                                           Evil
                                                           Rival &
                                                           Create
                                                           Mined
```

 - *challengePeriodBlocksInTime*: `challengePeriodBlocks` in time.
 - *t1*: the time that an honest party sends a tx of creating an edge (whether creating a layer zero edge or bisecting an edge at any level)
 - *k*: the delay between a tx is sent and being mined on Ethereum.
 - *t1 + k*: the time that the honest party's tx of creating an edge is mined
 - *tH*: the remaining unrivaled time that the honest party requires to win by confirming the valid assertion claim by time. `totalUnriveledTimeInTime(valid assertion claim) = challengePeriodBlocksInTime - tH`
 - *t1 + tH*: the time that the evil party sends the tx of rivaling the honest party's edge as well as creating a new edge. This will be mined at *t1 + k + tH*.
 - *tA*: the remaining unrivaled time that the evil party requires to win by confirming the malicious assertion claim by time. `totalUnriveledTimeInTime(malicious assertion claim) = challengePeriodBlocksInTime - tA`
 - *t1 + tH + tA*: the time that the evil party sends the confirmation tx by calling `confirmEdgeByTime` to confirm the malicious assertion claim. 
 - *t1 + k + tH*: the time that the honest party sends the confirmation tx by calling `confirmEdgeByTime` to confirm the valid assertion claim, also the time that the evil party's rivaling and the new edge creation is mined.
 - *t1 + k + tH + tA*: the time that the evil party's confirmation tx is mined.
 - *t1 + k + tH + k*: the time that the honest party's confirmation tx is mined.

 ```solidity
    function confirmEdgeByTime(bytes32 edgeId, AssertionStateData calldata claimStateData) public {
        ChallengeEdge storage topEdge = store.get(edgeId);
        if (!topEdg
Upgrades to EdgeChallengeManager using setChallengeManager can allow to confirm bad assertion
Audit: 2024-05-arbitrum-foundation-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-arbitrum-foundation/blob/main/src/rollup/RollupAdminLogic.sol#L326-L329


# Vulnerability details

## Description
The report is related to my other report related to EdgeChallengeManager (a.k.a. ECM) upgrade (`Upgrades to EdgeChallengeManager changing staking requirement can cause funds loss`), so you should read it first before continuing as I will not re-explain what was explained in my previous report to save both the reader and myself time.

This report is exploring the impact of upgrading the `EdgeChallengeManager.sol` entirely (using RollupAdminLogic::setChallengeManager) and the impact on the BoLD system overall. This is a new design, while before the stakeToken could be replaced (using `RollupAdminLogic::setStakeToken`), which is now being replaced by the fact that instead you replace the ECM as a whole (which might have a another stakeToken).

While the golang BoLD code is out-of-scope, since there is a key implication with how `EdgeChallengeManager.sol` and `RollupAdminLogic.sol` evolve in case of upgrade, I believe this should be considered in scope as involving directly those contract life cycle and seems to warrant `High` severity.

Validators are connected to a single instance ECM when they start. This can be seen at multiple places in the code but more importantly when creating the AssertionManager [Manager::NewManager](https://github.com/OffchainLabs/bold/blob/main/assertions/manager.go#L133), and same for the [assertion chain](https://github.com/OffchainLabs/bold/blob/main/assertions/manager.go#L126) which internally use the same [single ECM](https://github.com/OffchainLabs/bold/blob/main/chain-abstraction/sol-implementation/assertion_chain.go#L177).

The validators will keep pooling the blockchain and syncing any new assertion created, and create challenge when it detect that an assertion is wrong, this is done inside `sync.go`. In case an ECM upgrade occurs, the [following condition](https://github.com/OffchainLabs/bold/blob/main/assertions/sync.go#L418-L427) will trigger once the new ECM instance is officialized (RollupAdminLogic::setChallengeManager), which will have no real impact beside a `logging warning`.
```
	if args.canonicalParent.ChallengeManager != m.challengeManagerAddr {
		srvlog.Warn("Posted rival assertion, but could not challenge as challenge manager address did not match, "+
			"start a new server with the right challenge manager address", log.Ctx{
			"correctAssertion":                  postedRival.AssertionHash,
			"evilAssertion":                     args.invalidAssertion.AssertionHash,
			"expectedChallengeManagerAddress":   args.canonicalParent.ChallengeManager,
			"configuredChallengeManagerAddress": m.challengeManagerAddr,
		})
		return nil, nil
	}
```

We can see that the validator supporting BoLD will use the [following code](https://github.com/OffchainLabs/bold/blob/main/testing/endtoend/helpers_test.go#L38-L40
) most likely (well similar, as this