Farseen0/scar-pairs huggingface dataset:
HIGH severity: <a name="N"></a>[NFunctions not used internally could be marked external. *Instances (28)*:

Vulnerable code:
File: LSSVMPair.sol

323:     function getAssetRecipient() public view returns (address payable) {

344:     function getFeeRecipient() public view returns (address payable _feeRecipient) {

File: LSSVMPairFactory.sol

342:     function openLock() public {

347:     function closeLock() public {

499:     function getSettingsForPair(address pairAddress) public view returns (bool settingsEnabled, uint96 bps) {

513:     function toggleSettingsForCollection(address settings, address collectionAddress, bool enable) public {

529:     function enableSettingsForPair(address settings, address pairAddress) public {

546:     function disableSettingsForPair(address settings, address pairAddress) public {

File: RoyaltyEngine.sol

66:     function getCachedRoyaltySpec(address tokenAddress) public view returns (int16) {

77:     function bulkCacheSpecs(address[] calldata tokenAddresses, uint256[] calldata tokenIds, uint256[] calldata values)

100:     function getRoyalty(address tokenAddress, uint256 tokenId, uint256 value)

119:     function getRoyaltyView(address tokenAddress, uint256 tokenId, uint256 value)
LOW severity: <a name=""></a>`abi.encodePacked()` should not be used with dynamic types when passing the result to a hash function such as `keccak256()`. Use `abi.encode()` instead which will pad items to 32 bytes, which will [prevent hash collisions](https://docs.soliditylang.org/en/v0.8.13/abi-spec.html#non-standard-packed-mode) (e.g. `abi.encodePacked(0x123,0x456)` => `0x123456` => `abi.encodePacked(0x1,0x23456)`, but `abi.encode(0x123,0x456)`...

Vulnerable code:
File: property-checking/MerklePropertyChecker.sol

25:             if (!MerkleProof.verify(proof, root, keccak256(abi.encodePacked(ids[i])))) {
LOW severity: <a name=""></a>Empty Function Body - Consider commenting why. *Instances (32)*:

Vulnerable code:
File: LSSVMPairETH.sol

130:     function _preCallCheck(address) internal pure override {}

File: LSSVMPairFactory.sol

373:                 } catch {}

375:         } catch {}

379:         } catch {}

384:             } catch {}

385:         } catch {}

390:             } catch {}

391:         } catch {}

398:             } catch {}

399:         } catch {}

403:         } catch {}

410:     receive() external payable {}

File: LSSVMRouter.sol

488:     receive() external payable {}
LOW severity: <a name=""></a>Initializers could be front-run. Initializers could be front-run, allowing an attacker to either set their own values, take ownership of the contract, and in the best case forcing a re-deployment

*Instances (10)*:

Vulnerable code:
File: LSSVMPair.sol

132:     function initialize(

140:         __Ownable_init(_owner);

File: LSSVMPairFactory.sol

568:         _pair.initialize(msg.sender, _assetRecipient, _delta, _fee, _spotPrice);

596:         _pair.initialize(msg.sender, _assetRecipient, _delta, _fee, _spotPrice);

625:         _pair.initialize(msg.sender, _assetRecipient, _delta, _fee, _spotPrice);

649:         _pair.initialize(msg.sender, _assetRecipient, _delta, _fee, _spotPrice);

File: lib/OwnableWithTransferCallback.sol

24:     function __Ownable_init(address initialOwner) internal {
HIGH severity: <a name=""></a>Unsafe ERoperation(s). *Instances (7)*:

Vulnerable code:
File: LSSVMPairFactory.sol

576:             _nft.transferFrom(msg.sender, address(_pair), _initialNFTIDs[i]);

606:             _nft.transferFrom(msg.sender, address(_pair), _initialNFTIDs[i]);

673:             _nft.transferFrom(msg.sender, recipient, ids[i]);

File: LSSVMRouter.sol

525:         nft.transferFrom(from, to, id);

File: VeryFastRouter.sol

713:         nft.transferFrom(from, to, id);
MEDIUM severity: <a name=""></a>Centralization Risk for trusted owners. **Impact:**
Contracts have owners with privileged rights to perform admin tasks and need to be trusted to not perform malicious updates or drain funds.

*Instances (23)*:

Vulnerable code:
File: LSSVMPair.sol

582:     function changeSpotPrice(uint128 newSpotPrice) external onlyOwner {

595:     function changeDelta(uint128 newDelta) external onlyOwner {

610:     function changeFee(uint96 newFee) external onlyOwner {

625:     function changeAssetRecipient(address payable newRecipient) external onlyOwner {

640:     function call(address payable target, bytes calldata data) external onlyOwner {

672:     function multicall(bytes[] calldata calls, bool revertOnFail) external onlyOwner {

File: LSSVMPairERC20.sol

129:     function withdrawERC20(ERC20 a, uint256 amount) external override onlyOwner {

File: LSSVMPairETH.sol

90:     function withdrawAllETH() external onlyOwner {

100:     function withdrawETH(uint256 amount) public onlyOwner {

108:     function withdrawERC20(ERC20 a, uint256 amount) external override onlyOwner {
MEDIUM severity: <a name=""></a>Solmate's SafeTransferLib does not check for token contract's existence. There is a subtle difference between the implementation of solmate’s SafeTransferLib and OZ’s SafeERC20: OZ’s SafeERC20 checks if the token is a contract or not, solmate’s SafeTransferLib does not.
https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol#L9
`@dev Note...

Vulnerable code:
File: LSSVMPairERC20.sol

90:             token_.safeTransferFrom(msg.sender, _assetRecipient, inputAmountExcludingRoyalty - protocolFee);

94:                 token_.safeTransferFrom(msg.sender, royaltyRecipients[i], royaltyAmounts[i]);

102:                 token_.safeTransferFrom(msg.sender, address(factory()), protocolFee);

110:                 token().safeTransfer(_feeRecipient, tradeFeeAmount);

124:             token().safeTransfer(tokenRecipient, outputAmount);

130:         a.safeTransfer(msg.sender, amount);

File: LSSVMPairETH.sol

109:         a.safeTransfer(msg.sender, amount);

File: LSSVMPairFactory.sol

430:         token.safeTransfer(protocolFeeRecipient, amount);

600:             _token.safeTransferFrom(msg.sender, address(_pair), _initialTokenBalance);

632:             _nft.safeTransferFrom(msg.sender, address(_pair), _nftId, _initialNFTBalance, bytes(""));

653:             _token.safeTransferFrom(msg.sender, address(_pair), _initialTokenBalance);

658:             _nft.safeTransferFrom(msg.sender, address(_pair), _nftId, _initialNFTBalance, bytes(""));

691:         token.safeTransferFrom(msg.sender, recipient, amount);

706:         nft.safeTransferFrom(msg.sender, recipient, id, amount, bytes(""));
MEDIUM severity: Calls to `LimitOrderRegistry::newOrder` might revert due to overflow. **Description:** Reasonable input could cause an arithmetic overflow when opening new orders because large multiplications are performed on variables defined as `uint128` instead of `uint256`. Specifically, in `LimitOrderRegistry::_mintPosition` and `LimitOrderRegistry::_addToPosition` the...

Vulnerable code:
uint128 amount0Min = amount0 == 0 ? 0 : (amount0 * 0.9999e18) / 1e18;
uint128 amount1Min = amount1 == 0 ? 0 : (amount1 * 0.9999e18) / 1e18;

function test_OverflowingNewOrder() public {
    uint96 amount = 340_316_398_560_794_542_918;
    address msgSender = 0xE0b906ae06BfB1b54fad61E222b2E324D51e1da6;
    deal(address(USDC), msgSender, amount);
    vm.startPrank(msgSender);
    USDC.approve(address(registry), amount);

    registry.newOrder(USDC_WETH_05_POOL, 204900, amount, true, 0);
}

uint128 amount0Min = amount0 == 0 ? 0 : uint128((uint256(amount0) * 0.9999e18) / 1e18);
uint128 amount1Min = amount1 == 0 ? 0 : uint128((uint256(amount1) * 0.9999e18) / 1e18);
MEDIUM severity: New orders on a given pool in the opposite direction, separated by zero/one tick space, are not possible until previous `BatchOrder` is removed from the order book. **Description:** New order creation will revert with `LimitOrderRegistry__DirectionMisMatch()` if the direction opposes any existing orders at the current tick price. Due to the calculation of lower/upper ticks, this also applies to orders separated by one tick space. In the event price deviation...

Vulnerable code:
function testOppositeOrders() external {
    uint256 amount = 1_000e6;
    deal(address(USDC), address(this), amount);

    // Current tick 204332
    // 204367
    // Current block 16371089
    USDC.approve(address(registry), amount);
    registry.newOrder(USDC_WETH_05_POOL, 204910, uint96(amount), true, 0);

    // Make a large swap to move the pool tick.
    address[] memory path = new address[](2);
    path[0] = address(WETH);
    path[1] = address(USDC);

    uint24[] memory poolFees = new uint24[](1);
    poolFees[0] = 500;

    (bool upkeepNeeded, bytes memory performData) = registry.checkUpkeep(abi.encode(USDC_WETH_05_POOL));

    uint256 swapAmount = 2_000e18;
    deal(address(WETH), address(this), swapAmount);
    _swap(path, poolFees, swapAmount);

    (upkeepNeeded, performData) = registry.checkUpkeep(abi.encode(USDC_WETH_05_POOL));

    assertTrue(upkeepNeeded, "Upkeep should be needed.");

    // registry.performUpkeep(performData);

    amount = 2_000e17;
    deal(address(WETH), address(this), amount);
    WETH.approve(address(registry), amount);
    int24 target = 204910 - USDC_WETH_05_POOL.tickSpacing();
    vm.expectRevert(
        abi.encodeWithSelector(LimitOrderRegistry.LimitOrderRegistry__DirectionMisMatch.selector)
    );
    registry.newOrder(USDC_WETH_05_POOL, target, uint96(amount), false, 0);
}
MEDIUM severity: Calls to `LimitOrderRegistry::cancelOrder` might revert due to overflow. **Description:** Reasonable input could cause an arithmetic overflow when cancelling orders because large multiplications are performed on variables defined as `uint128` instead of `uint256`. Specifically, when calculating the liquidity percentage to take from a position in...

Vulnerable code:
uint128 depositAmount = batchIdToUserDepositAmount[batchId][sender];
if (depositAmount == 0) revert LimitOrderRegistry__UserNotFound(sender, batchId);

// Remove one from the userCount.
order.userCount--;

// Zero out user balance.
delete batchIdToUserDepositAmount[batchId][sender];

uint128 orderAmount;
if (order.direction) {
    orderAmount = order.token0Amount;
    if (orderAmount == depositAmount) {
        liquidityPercentToTake = 1e18;
        // Update order tokenAmount.
        order.token0Amount = 0;
    } else {
        liquidityPercentToTake = (1e18 * depositAmount) / orderAmount; // @audit - overflow
        // Update order tokenAmount.
        order.token0Amount = orderAmount - depositAmount;
    }
} else {
    orderAmount = order.token1Amount;
    if (orderAmount == depositAmount) {
        liquidityPercentToTake = 1e18;
        // Update order tokenAmount.
        order.token1Amount = 0;
    } else {
        liquidityPercentToTake = (1e18 * depositAmount) / orderAmount; // @audit - overflow
        // Update order tokenAmount.
        order.token1Amount = orderAmount - depositAmount;
    }
}

liquidityPercentToTake = (1e18 * uint256(depositAmount)) / orderAmount;
LOW severity: Perform additional validation on Chainlink fast gas feed. **Description:** When consuming data feeds provided by Chainlink, it is important to validate a number of thresholds and return values. Without this, it is possible for a consuming contract to use stale or otherwise incorrect/invalid data. `LimitOrderRegistry` currently correctly validates the...

Vulnerable code:
require(signedGasPrice > 0, "Negative gas price");
require(signedGasPrice < maxGasPrice, "Upper gas price bound breached");
require(signedGasPrice > minGasPrice, "Lower gas price bound breached");
require(answeredInRound >= roundID, "Round incomplete");
LOW severity: Withdrawing native assets may revert if wrapped native balance is zero. **Description:** The function `LimitOrderRegistry::withdrawNative` allows the owner to withdraw native and wrapped native assets from the contract. If balances of both are zero, calls to this function revert as there is nothing to withdraw.

A zero value call with transfer non-zero wrapped native...

Vulnerable code:
/**
 * @notice Allows owner to withdraw wrapped native and native assets from this contract.
 */
function withdrawNative() external onlyOwner {
    uint256 wrappedNativeBalance = WRAPPED_NATIVE.balanceOf(address(this));
    uint256 nativeBalance = address(this).balance;
    // Make sure there is something to withdraw.
    if (wrappedNativeBalance == 0 && nativeBalance == 0) revert LimitOrderRegistry__ZeroNativeBalance();
    WRAPPED_NATIVE.safeTransfer(owner, WRAPPED_NATIVE.balanceOf(address(this)));
    payable(owner).transfer(address(this).balance);
}

if (wrappedNativeBalance == 0 && nativeBalance == 0) revert LimitOrderRegistry__ZeroNativeBalance();
LOW severity: Fee-on-transfer/deflationary tokens will not be supported. **Description:** When creating a new order, `LimitOrderRegistry::newOrder` assumes that the amount of deposited tokens is equal to the
function parameter plus the balance before the deposit. For tokens which take a fee on every transfer, this assumption does not hold and so the true amount...

Vulnerable code:
uint128 amount0Min = amount0 == 0 ? 0 : (amount0 * 0.9999e18) / 1e18;
HIGH severity: Improvements to use of ternary operator. There is currently one [instance](https://github.com/crispymangoes/uniswap-v3-limit-orders/blob/83f5db9cb90926c11ee6ce872dc165b7f600f3d8/src/LimitOrderRegistry.sol#L903) of ternary operator usage that can be simplified, with the boolean expression being evaluated directly:

should be changed...

Vulnerable code:
bool direction = targetTick > node.tickUpper ? true : false;

if (direction)
    assetIn = poolToData[pool].token0;
else assetIn = poolToData[pool].token1;

assetIn = direction ? poolToData[pool].token0 : poolToData[pool].token1;
MEDIUM severity: Should ensure uniqueness of the tokens of Wells. **Description:** The current implementation does not enforce uniqueness in the tokens of Wells.
Anyone can call `Aquifer::boreWell()` with malicious `Well` implementation to set up a trap for victims.
Through communication with the protocol team, it is understood that all Wells are considered...

Vulnerable code:
function skim(address recipient) external nonReentrant returns (uint[] memory skimAmounts) {
    IERC20[] memory _tokens = tokens();
    uint[] memory reserves = _getReserves(_tokens.length);
    skimAmounts = new uint[](_tokens.length);
    for (uint i; i < _tokens.length; ++i) {
        skimAmounts[i] = _tokens[i].balanceOf(address(this)) - reserves[i];
        if (skimAmounts[i] > 0) {
            _tokens[i].safeTransfer(recipient, skimAmounts[i]);
        }
    }
}
MEDIUM severity: `LibLastReserveBytes::storeLastReserves` has no check for reserves being too large. **Description:** After every liquidity event & swap, the `IPump::update`()` is called.
To update the pump, the `LibLastReserveBytes::storeLastReserves` function is used. This packs the reserve data into `bytes32` slots in storage.
A slot is then broken down into the following components:
- 1 byte...

Vulnerable code:
src\libraries\LibLastReserveBytes.sol
21:    uint8 n = uint8(reserves.length);
22:    if (n == 1) {
23:        assembly {
24:            sstore(slot, or(or(shl(208, lastTimestamp), shl(248, n)), shl(104, shr(152, mload(add(reserves, 32))))))
25:        }
26:        return;
27:    }
28:    assembly {
29:        sstore(
30:            slot,
31:            or(
32:                or(shl(208, lastTimestamp), shl(248, n)),
33:                or(shl(104, shr(152, mload(add(reserves, 32)))), shr(152, mload(add(reserves, 64))))
34:            )
35:        )
36:        // slot := add(slot, 32)
37:    }

require(reserves[0] <= type(uint128).max, "ByteStorage: too large");

function testStoreAndReadTwo() public {
    uint40 lastTimeStamp = 12345363;
    bytes16[] memory reserves = new bytes16[](2);
    reserves[0] = 0xffffffffffffffffffffffffffffffff; // This is too big!
    reserves[1] = 0x11111111111111111111111100000000;
    RESERVES_STORAGE_SLOT.storeLastReserves(lastTimeStamp, reserves);
    (
        uint8 n,
        uint40 _lastTimeStamp,
        bytes16[] memory _reserves
    ) = RESERVES_STORAGE_SLOT.readLastReserves();
    assertEq(2, n);
    assertEq(lastTimeStamp, _lastTimeStamp);
    assertEq(reserves[0], _reserves[0]); // This will fail
    assertEq(reserves[1], _reserves[1]);
    assertEq(reserves.length, _reserves.length);
}
LOW severity: TWAP is incorrect when only 1 update has occurred. **Description:** If there has only been a single update to the pump, `GeoEmaAndCumSmaPump::readTwaReserves` will return an incorrect value.

**Impact:** Given this affects only one oracle update when the pump is still ramping up, we evaluate the severity to LOW.

**Proof of...

Vulnerable code:
// NOTE: place in `Pump.Update.t.sol`
function testTWAReservesIsWrong() public {
    increaseTime(12); // increase 12 seconds

    bytes memory startCumulativeReserves = pump.readCumulativeReserves(
        address(mWell)
    );
    uint256 lastTimestamp = block.timestamp;
    increaseTime(120); // increase 120 seconds aka 10 blocks

    (uint[] memory twaReserves, ) = pump.readTwaReserves(
        address(mWell),
        startCumulativeReserves,
        lastTimestamp
    );

    assertApproxEqAbs(twaReserves[0], 1e6, 1);
    assertApproxEqAbs(twaReserves[1], 2e6, 1);

    vm.prank(user);
    // gonna double it
    // so our TWAP should now be ~1.5 & 3
    b[0] = 2e6;
    b[1] = 4e6;
    mWell.update(address(pump), b, new bytes(0));

    increaseTime(120); // increase 12 seconds aka 10 blocks
    (twaReserves, ) = pump.readTwaReserves(
        address(mWell),
        startCumulativeReserves,
        lastTimestamp
    );

    // the reserves of b[0]:
    // - 1e6 * 10 blocks
    // - 2e6 * 10 blocks
    // average reserves over 20 blocks:
    // - 15e5
    // assertApproxEqAbs(twaReserves[0], 1.5e5, 1);
    // instead, we get:
    // b[0] = 2070529

    // the reserves of b[1]:
    // - 2e6 * 10 blocks
    // - 4e6 * 10 blocks
    // average reserves over 20 blocks:
    // - 3e6
    assertApproxEqAbs(twaReserves[1], 3e6, 1);
    // instead, we get:
    // b[1] = 4141059
}
HIGH severity: Incorrect sload in LibBytes. **Description:** The function `storeUint128` in `LibBytes` intends to pack uint128 `reserves` starting at the given slot but will overwrite the final slot if [storing an odd number of reserves](https://github.com/BeanstalkFarms/Wells/blob/e5441fc78f0fd4b77a898812d0fd22cb43a0af55/src/libraries/LibByt...

Vulnerable code:
// NOTE: Add to LibBytes.t.sol
function test_exploitStoreAndRead() public {
    // Write to storage slot to demonstrate overwriting existing values
    // In this case, 420 will be stored in the lower 128 bits of the last slot
    bytes32 slot = RESERVES_STORAGE_SLOT;
    uint256 maxI = (NUM_RESERVES_MAX - 1) / 2;
    uint256 storeValue = 420;
    assembly {
        sstore(add(slot, mul(maxI, 32)), storeValue)
    }

    // Read reserves and assert the final reserve is 420
    uint[] memory reservesBefore = LibBytes.readUint128(RESERVES_STORAGE_SLOT, NUM_RESERVES_MAX);
    emit log_named_array("reservesBefore", reservesBefore);

    // Set up reserves to store, but only up to NUM_RESERVES_MAX - 1 as we have already stored a value in the last 128 bits of the last slot
    uint[] memory reserves = new uint[](NUM_RESERVES_MAX - 1);
    for (uint i = 1; i < NUM_RESERVES_MAX; i++) {
        reserves[i-1] = i;
    }

    // Log the last reserve before the store, perhaps from other implementations which don't always act on the entire reserves length
    uint256 t;
    assembly {
        t := shr(128, shl(128, sload(add(slot, mul(maxI, 32)))))
    }
    emit log_named_uint("final slot, lower 128 bits before", t);

    // Store reserves
    LibBytes.storeUint128(RESERVES_STORAGE_SLOT, reserves);

    // Re-read reserves and compare
    uint[] memory reserves2 = LibBytes.readUint128(RESERVES_STORAGE_SLOT, NUM_RESERVES_MAX);

    emit log_named_array("reserves", reserves);
    emit log_named_array("reserves2", reserves2);

    // But wait, what about the last reserve
    assembly {
        t := shr(128, shl(128, sload(add(slot, mul(maxI, 32)))))
    }

    // Turns out it was overwritten by the last store as it calculates the sload incorrectly
    emit log_named_uint("final slot, lower 128 bits after", t);
}

reservesBefore: [0, 0, 0, 0, 0, 0, 0, 420]
final slot, lower 128 bits before: 420
reserves: [1, 2, 3, 4, 5, 6, 7]
reserves2: [1, 2, 3, 4, 5, 6, 7, 0]
final slot, lower 128 bits after: 0

reservesBefore: [0, 0, 0, 0, 0, 0, 0, 420]
final slot, lower 128 bits before: 420
reserves: [1, 2, 3, 4, 5, 6, 7]
reserves2: [1, 2, 3, 4, 5, 6, 7, 420]
final slot, lower 128 bits after: 420
HIGH severity: Non-standard ERtokens are not supported. **Severity:** Medium

**Description:** The protocol implemented a function `deposit()` to allow users to deposit.

Looking at the line L49, we can see that the protocol assumes `amount` of tokens were transferred.
But this does not hold true for some non-standard ERC20 tokens like fee-on-transfer...

Vulnerable code:
DepositVault.sol
37:     function deposit(uint256 amount, address tokenAddress) public payable {
38:         require(amount > 0 || msg.value > 0, "Deposit amount must be greater than 0");
39:         if(msg.value > 0) {
40:             require(tokenAddress == address(0), "Token address must be 0x0 for ETH deposits");
41:             uint256 depositIndex = deposits.length;
42:             deposits.push(Deposit(payable(msg.sender), msg.value, tokenAddress));
43:             emit DepositMade(msg.sender, depositIndex, msg.value, tokenAddress);
44:         } else {
45:             require(tokenAddress != address(0), "Token address must not be 0x0 for token deposits");
46:             IERC20 token = IERC20(tokenAddress);
47:             token.safeTransferFrom(msg.sender, address(this), amount);
48:             uint256 depositIndex = deposits.length;
49:             deposits.push(Deposit(payable(msg.sender), amount, tokenAddress));//@audit-issue fee-on-transfer, rebalancing tokens will cause problems
50:             emit DepositMade(msg.sender, depositIndex, amount, tokenAddress);
51:
52:         }
53:     }
LOW severity: Unnecessary parameter amount in withdraw function. **Severity:** Informational

**Description:** The function `withdraw()` has a parameter `amount` but we don't understand the necessity of this parameter.
At line L67, the amount is required to be the same to the whole deposit amount. This means the user does not have a flexibility to choose the...

Vulnerable code:
DepositVault.sol
59:     function withdraw(uint256 amount, uint256 nonce, bytes memory signature, address payable recipient) public {
60:         require(nonce < deposits.length, "Invalid deposit index");
61:         Deposit storage depositToWithdraw = deposits[nonce];
62:         bytes32 withdrawalHash = getWithdrawalHash(Withdrawal(amount, nonce));
63:         address signer = withdrawalHash.recover(signature);
64:         require(signer == depositToWithdraw.depositor, "Invalid signature");
65:         require(!usedWithdrawalHashes[withdrawalHash], "Withdrawal has already been executed");
66:         require(amount == depositToWithdraw.amount, "Withdrawal amount must match deposit amount");//@audit-info only full withdrawal is allowed
67:
68:         usedWithdrawalHashes[withdrawalHash] = true;
69:         depositToWithdraw.amount = 0;
70:
71:         if(depositToWithdraw.tokenAddress == address(0)){
72:             recipient.transfer(amount);
73:         } else {
74:             IERC20 token = IERC20(depositToWithdraw.tokenAddress);
75:             token.safeTransfer(recipient, amount);
76:         }
77:
78:         emit WithdrawalMade(recipient, amount);
79:     }
HIGH severity: Use != 0 instead of > 0 for unsigned integer comparison. **Client:**
Fixed.

**Cyfrin:** Verified in commit [b21d23e](https://github.com/HyperGood/woosh-contracts/commit/b21d23e661b0f25f0e757dc00ee90e4464730b1b).

Vulnerable code:
File: DepositVault.sol

38:         require(amount > 0 || msg.value > 0, "Deposit amount must be greater than 0");

38:         require(amount > 0 || msg.value > 0, "Deposit amount must be greater than 0");

39:         if(msg.value > 0)

85:         require(depositToWithdraw.amount > 0, "Deposit has already been withdrawn");