msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: It's possible to create pairs that cannot be taken over
Severity: medium

Description:
It's possible to create pairs that cannot be taken over and DOS a pair forever.\\nA pair is created by calling createPair() which takes the initial parameters of the pair as inputs but the initial parameters are never verified, which makes it possible for an attacker to create a token pair that's impossible to recover via takeOverPool().\\nThere's more ways to create a pair that cannot be taken over, a simple example is to set all of the initial parameters to the maximum possible value:\\n```\\nuint112 virtualEth = type(uint112).max;\\nuint112 bootstrapEth = type(uint112).max;\\nuint112 initialEth = type(uint112).max;\\nuint112 initialTokenMatch = type(uint112).max;\\n```\\n\\nThis will make takeOverPool() revert for overflow on the internal call to _tokenAmountsForLiquidityBootstrap:\\n```\\nuint256 k = virtualEth * initialTokenMatch;\\n tokenAmtForAmm = (k * bootstrapEth) / (totalEth * totalEth);\\n```\\n\\nHere `virtualEth`, `initialTokenMatch` and `bootstrapEth` are all setted to `type(uint112).max`. The multiplication `virtualEth` * `initialTokenMatch` * `bootstrapEth` performed to calculate `tokenAmtForAmm` will revert for overflow because `2^112 * 2^112 * 2^112 = 2^336` which is bigger than `2^256`.

Impact:
Creation of new pairs can be DOSed forever.

Recommended mitigation:
Validate a pair initial parameters and mint liquidity on pool creation.
Vulnerability: Module's gas yield can never be claimed and all yield will be lost
Severity: high

Description:
Module's gas yield can never be claimed\\nThe protocol is meant to be deployed on blast, meaning that the gas and ether balance accrue yield.\\nBy default these yield settings for both ETH and GAS yields are set to VOID as default, meaning that unless we configure the yield mode to claimable, we will be unable to recieve the yield. The protocol never sets gas to claimable for the modules, and the governor of the contract is the auction house, the auction house also does not implement any function to set the modules gas yield to claimable.\\n```\\n constructor(address auctionHouse_) LinearVesting(auctionHouse_) BlastGas(auctionHouse_) {}\\n```\\n\\nThe constructor of both BlastLinearVesting and BlastEMPAM set the auction house here `BlastGas(auctionHouse_)` if we look at this contract we can observe the above.\\nBlastGas.sol\\n```\\n    constructor(address parent_) {\\n        // Configure governor to claim gas fees\\n        IBlast(0x4300000000000000000000000000000000000002).configureGovernor(parent_);\\n    }\\n```\\n\\nAs we can see above, the governor is set in constructor, but we never set gas to claimable. Gas yield mode will be in its default mode which is VOID, the modules will not accue gas yields. Since these modules never set gas yield mode to claimable, the auction house cannot claim any gas yield for either of the contracts. Additionally the auction house includes no function to configure yield mode, the auction house contract only has a function to claim the gas yield but this will revert since the yield mode for these module contracts will be VOID.

Impact:
Gas yields will never acrue and the yield will forever be lost

Recommended mitigation:
change the following in BlastGas contract, this will set the gas yield of the modules to claimable in the constructor and allowing the auction house to claim gas yield.\\n```\\ninterface IBlast {\\n   function configureGovernor(address governor_) external;\\n   function configureClaimableGas() external; \\n}\\n\\nabstract contract BlastGas {\\n    // ========== CONSTRUCTOR ========== //\\n\\n    constructor(address parent_) {\\n        // Configure governor to claim gas fees\\n       IBlast(0x4300000000000000000000000000000000000002).configureClaimableGas();\\n       IBlast(0x4300000000000000000000000000000000000002).configureGovernor(parent_);\\n    }\\n}\\n```\\n
Vulnerability: Bidders can not claim their bids if the auction creator claims the proceeds.
Severity: high

Description:
Before the batch `auction` begins, the `auction` creator should `prefund` `base` tokens to the `auction` house. During the `auction`, `bidders` transfer `quote` tokens to the `auction` house. After the `auction` settles,\\n`Bidders` can claim their `bids` and either to receive `base` tokens or `retrieve` their `quote` tokens.\\nThe `auction creator` can receive the `quote` tokens and `retrieve` the remaining `base` tokens.\\nThere is no specific order for these two operations.\\nHowever, if the `auction creator` claims the `proceeds`, `bidders` can not claim their `bids` anymore. Consequently, their `funds` will remain locked in the `auction house`.\\nWhen the `auction creator` claims `Proceeds`, the `auction status` changes to `Claimed`.\\n```\\nfunction _claimProceeds(uint96 lotId_)\\n    internal\\n    override\\n    returns (uint96 purchased, uint96 sold, uint96 payoutSent)\\n{\\n    auctionData[lotId_].status = Auction.Status.Claimed;\\n}\\n```\\n\\nOnce the `auction status` has transitioned to `Claimed`, there is indeed no way to change it back to `Settled`.\\nHowever, `bidders` can only claim their `bids` when the `auction status` is `Settled`.\\n```\\nfunction claimBids(\\n    uint96 lotId_,\\n    uint64[] calldata bidIds_\\n)\\n    external\\n    override\\n    onlyInternal\\n    returns (BidClaim[] memory bidClaims, bytes memory auctionOutput)\\n{\\n    _revertIfLotInvalid(lotId_);\\n    _revertIfLotNotSettled(lotId_);   // @audit, here\\n\\n    return _claimBids(lotId_, bidIds_);\\n}\\n```\\n\\nPlease add below test to the `test/modules/auctions/claimBids.t.sol`.\\n```\\nfunction test_claimProceeds_before_claimBids()\\n    external\\n    givenLotIsCreated\\n    givenLotHasStarted\\n    givenBidIsCreated(_BID_AMOUNT_UNSUCCESSFUL, _BID_AMOUNT_OUT_UNSUCCESSFUL)\\n    givenBidIsCreated(_BID_PRICE_TWO_AMOUNT, _BID_PRICE_TWO_AMOUNT_OUT)\\n    givenBidIsCreated(_BID_PRICE_TWO_AMOUNT, _BID_PRICE_TWO_AMOUNT_OUT)\\n    givenBidIsCreated(_BID_PRICE_TWO_AMOUNT, _BID_PRICE_TWO_AMOUNT_OUT)\\n    givenBidIsCreated(_BID_PRICE_TWO_AMOUNT, _BID_PRICE_TWO_AMOUNT_OUT)\\n    givenBidIsCreated(_BID_PRICE_TWO_AMOUNT, _BID_PRICE_TWO_AMOUNT_OUT)\\n    givenBidIsCreated(_BID_PRICE_TWO_AMOUNT, _BID_PRICE_TWO_AMOUNT_OUT)\\n    givenLotHasConcluded\\n    givenPrivateKeyIsSubmitted\\n    givenLotIsDecrypted\\n    givenLotIsSettled\\n{\\n    uint64 bidId = 1;\\n\\n    uint64[] memory bidIds = new uint64[](1);\\n    bidIds[0] = bidId;\\n\\n    // Call the function\\n    vm.prank(address(_auctionHouse));\\n    _module.claimProceeds(_lotId);\\n\\n\\n    bytes memory err = abi.encodeWithSelector(EncryptedMarginalPriceAuctionModule.Auction_WrongState.selector, _lotId);\\n    vm.expectRevert(err);\\n    vm.prank(address(_auctionHouse));\\n    _module.claimBids(_lotId, bidIds);\\n}\\n```\\n

Impact:
Users' funds could be locked.

Recommended mitigation:
Allow `bidders` to claim their `bids` even when the `auction status` is `Claimed`.
Vulnerability: Downcasting to uint96 can cause assets to be lost for some tokens
Severity: high

Description:
Downcasting to uint96 can cause assets to be lost for some tokens\\nAfter summing the individual bid amounts, the total bid amount is downcasted to uint96 without any checks\\n```\\n            settlement_.totalIn = uint96(result.totalAmountIn);\\n```\\n\\nuint96 can be overflowed for multiple well traded tokens:\\nEg:\\nshiba inu : current price = $0.00003058 value of type(uint96).max tokens ~= 2^96 * 0.00003058 / 10^18 == 2.5 million $\\nHence auctions that receive more than type(uint96).max amount of tokens will be downcasted leading to extreme loss for the auctioner

Impact:
The auctioner will suffer extreme loss in situations where the auctions bring in >uint96 amount of tokens

Recommended mitigation:
Use a higher type or warn the user's of the limitations on the auction sizes
Vulnerability: Incorrect `prefundingRefund` calculation will disallow claiming
Severity: high

Description:
Incorrect `prefundingRefund` calculation will lead to underflow and hence disallowing claiming\\nThe `prefundingRefund` variable calculation inside the `claimProceeds` function is incorrect\\n```\\n    function claimProceeds(\\n        uint96 lotId_,\\n        bytes calldata callbackData_\\n    ) external override nonReentrant {\\n        \\n        // rest of code\\n\\n        (uint96 purchased_, uint96 sold_, uint96 payoutSent_) =\\n            _getModuleForId(lotId_).claimProceeds(lotId_);\\n\\n        // rest of code.\\n\\n        // Refund any unused capacity and curator fees to the address dictated by the callbacks address\\n        // By this stage, a partial payout (if applicable) and curator fees have been paid, leaving only the payout amount (`totalOut`) remaining.\\n        uint96 prefundingRefund = routing.funding + payoutSent_ - sold_;\\n        unchecked {\\n            routing.funding -= prefundingRefund;\\n        }\\n```\\n\\nHere `sold` is the total base quantity that has been `sold` to the bidders. Unlike required, the `routing.funding` variable need not be holding `capacity + (0,curator fees)` since it is decremented every time a payout of a bid is claimed\\n```\\n    function claimBids(uint96 lotId_, uint64[] calldata bidIds_) external override nonReentrant {\\n        \\n        // rest of code.\\n\\n            if (bidClaim.payout > 0) {\\n \\n                // rest of code\\n\\n                // Reduce funding by the payout amount\\n                unchecked {\\n                    routing.funding -= bidClaim.payout;\\n                }\\n```\\n\\nExample\\nCapacity = 100 prefunded, hence routing.funding == 100 initially Sold = 90 and no partial fill/curation All bidders claim before the claimProceed function is invoked Hence routing.funding = 100 - 90 == 10 When claimProceeds is invoked, underflow and revert:\\nuint96 prefundingRefund = routing.funding + payoutSent_ - sold_ == 10 + 0 - 90

Impact:
Claim proceeds function is broken. Sellers won't be able to receive the proceedings

Recommended mitigation:
Change the calculation to:\\n```\\nuint96 prefundingRefund = capacity - sold_ + curatorFeesAdjustment (how much was prefunded initially - how much will be sent out based on capacity - sold)\\n```\\n
Vulnerability: User's can be grieved by not submitting the private key
Severity: medium

Description:
User's can be grieved by not submitting the private key\\nBids cannot be refunded once the auction concludes. And bids cannot be claimed until the auction has been settled. Similarly a EMPAM auction cannot be cancelled once started.\\n```\\n    function claimBids(\\n        uint96 lotId_,\\n        uint64[] calldata bidIds_\\n    )\\n        external\\n        override\\n        onlyInternal\\n        returns (BidClaim[] memory bidClaims, bytes memory auctionOutput)\\n    {\\n        // Standard validation\\n        _revertIfLotInvalid(lotId_);\\n        _revertIfLotNotSettled(lotId_);\\n```\\n\\n```\\n    function refundBid(\\n        uint96 lotId_,\\n        uint64 bidId_,\\n        address caller_\\n    ) external override onlyInternal returns (uint96 refund) {\\n        // Standard validation\\n        _revertIfLotInvalid(lotId_);\\n        _revertIfBeforeLotStart(lotId_);\\n        _revertIfBidInvalid(lotId_, bidId_);\\n        _revertIfNotBidOwner(lotId_, bidId_, caller_);\\n        _revertIfBidClaimed(lotId_, bidId_);\\n        _revertIfLotConcluded(lotId_);\\n```\\n\\n```\\n    function _cancelAuction(uint96 lotId_) internal override {\\n        // Validation\\n        // Batch auctions cannot be cancelled once started, otherwise the seller could cancel the auction after bids have been submitted\\n        _revertIfLotActive(lotId_);\\n```\\n\\n```\\n    function cancelAuction(uint96 lotId_) external override onlyInternal {\\n        // Validation\\n        _revertIfLotInvalid(lotId_);\\n        _revertIfLotConcluded(lotId_);\\n```\\n\\n```\\n    function _settle(uint96 lotId_)\\n        internal\\n        override\\n        returns (Settlement memory settlement_, bytes memory auctionOutput_)\\n    {\\n        // Settle the auction\\n        // Check that auction is in the right state for settlement\\n        if (auctionData[lotId_].status != Auction.Status.Decrypted) {\\n            revert Auction_WrongState(lotId_);\\n        }\\n```\\n\\nFor EMPAM auctions, the private key associated with the auction has to be submitted before the auction can be settled. In auctions where the private key is held by the seller, they can grief the bidder's or in cases where a key management solution is used, both seller and bidder's can be griefed by not submitting the private key.

Impact:
User's will not be able to claim their assets in case the private key holder doesn't submit the key for decryption

Recommended mitigation:
Acknowledge the risk involved for the seller and bidder
Vulnerability: Bidder's payout claim could fail due to validation checks in LinearVesting
Severity: medium

Description:
Bidder's payout claim will fail due to validation checks in LinearVesting after the expiry timestamp\\nBidder's payout are sent by internally calling the `_sendPayout` function. In case the payout is a derivative which has already expired, this will revert due to the validation check of `block.timestmap < expiry` present in the mint function of LinearVesting derivative\\n```\\n    function _sendPayout(\\n        address recipient_,\\n        uint256 payoutAmount_,\\n        Routing memory routingParams_,\\n        bytes memory\\n    ) internal {\\n        \\n        if (fromVeecode(derivativeReference) == bytes7("")) {\\n            Transfer.transfer(baseToken, recipient_, payoutAmount_, true);\\n        }\\n        else {\\n            \\n            DerivativeModule module = DerivativeModule(_getModuleIfInstalled(derivativeReference));\\n\\n            Transfer.approve(baseToken, address(module), payoutAmount_);\\n\\n=>          module.mint(\\n                recipient_,\\n                address(baseToken),\\n                routingParams_.derivativeParams,\\n                payoutAmount_,\\n                routingParams_.wrapDerivative\\n            );\\n```\\n\\n```\\n    function mint(\\n        address to_,\\n        address underlyingToken_,\\n        bytes memory params_,\\n        uint256 amount_,\\n        bool wrapped_\\n    )\\n        external\\n        virtual\\n        override\\n        returns (uint256 tokenId_, address wrappedAddress_, uint256 amountCreated_)\\n    {\\n        if (amount_ == 0) revert InvalidParams();\\n\\n        VestingParams memory params = _decodeVestingParams(params_);\\n\\n        if (_validate(underlyingToken_, params) == false) {\\n            revert InvalidParams();\\n        }\\n```\\n\\n```\\n    function _validate(\\n        address underlyingToken_,\\n        VestingParams memory data_\\n    ) internal view returns (bool) {\\n        \\n        // rest of code.\\n\\n=>      if (data_.expiry < block.timestamp) return false;\\n\\n\\n        // Check that the underlying token is not 0\\n        if (underlyingToken_ == address(0)) return false;\\n\\n\\n        return true;\\n    }\\n```\\n\\nHence the user's won't be able to claim their payouts of an auction once the derivative has expired. For EMPAM auctions, a seller can also wait till this timestmap passes before revealing their private key which will disallow bidders from claiming their rewards.

Impact:
Bidder's won't be able claim payouts from auction after the derivative expiry timestamp

Recommended mitigation:
Allow to mint tokens even after expiry of the vesting token / deploy the derivative token first itself and when making the payout, transfer the base token directly incase the expiry time is passed
Vulnerability: Settlement of batch auction can exceed the gas limit
Severity: medium

Description:
Settlement of batch auction can exceed the gas limit, making it impossible to settle the auction.\\nWhen a batch auction (EMPAM) is settled, to calculate the lot marginal price, the contract iterates over all bids until the capacity is reached or a bid below the minimum price is found.\\nAs some of the operations performed in the loop are gas-intensive, the contract may run out of gas if the number of bids is too high.\\nNote that additionally, there is another loop in the `_settle` function that iterates over all the remaining bids to delete them from the queue. While this loop consumes much less gas per iteration and would require the number of bids to be much higher to run out of gas, it adds to the problem.\\nChange the minimum bid percent to 0.1% in the `EmpaModuleTest` contract in `EMPAModuleTest.sol`.\\n```\\n// Remove the line below\\n   uint24 internal constant _MIN_BID_PERCENT = 1000; // 1%\\n// Add the line below\\n   uint24 internal constant _MIN_BID_PERCENT = 100; // 0.1%\\n```\\n\\nAdd the following code to the contract `EmpaModuleSettleTest` in `settle.t.sol` and run `forge test --mt test_settleOog`.\\n```\\nmodifier givenBidsCreated() {\\n    uint96 amountOut = 0.01e18;\\n    uint96 amountIn = 0.01e18;\\n    uint256 numBids = 580;\\n\\n    for (uint256 i = 0; i < numBids; i++) {\\n        _createBid(_BIDDER, amountIn, amountOut);\\n    }\\n    \\n    _;\\n}\\n\\nfunction test_settleOog() external\\n    givenLotIsCreated\\n    givenLotHasStarted\\n    givenBidsCreated\\n    givenLotHasConcluded\\n    givenPrivateKeyIsSubmitted\\n    givenLotIsDecrypted\\n{        \\n    uint256 gasBefore = gasleft();\\n    _settle();\\n\\n    assert(gasBefore - gasleft() > 30_000_000);\\n}\\n```\\n

Impact:
Settlement of batch auction will revert, causing sellers and bidders to lose their funds.

Recommended mitigation:
An easy way to tackle the issue would be to change the `_MIN_BID_PERCENT` value from 10 (0.01%) to 1000 (1%) in the `EMPAM.sol` contract, which would limit the number of iterations to 100.\\nA more appropriate solution, if it is not acceptable to increase the min bid percent, would be to change the settlement logic so that can be handled in batches of bids to avoid running out of gas.\\nIn both cases, it would also be recommended to limit the number of decrypted bids that can be deleted from the queue in a single transaction.
Vulnerability: Liquidation bonus scales exponentially instead of linearly.
Severity: medium

Description:
Liquidation bonus scales exponentially instead of linearly.\\nLet's look at the code of `getLiquidationBonus`\\n```\\n    function getLiquidationBonus(\\n        address token,\\n        uint256 borrowedAmount,\\n        uint256 times\\n    ) public view returns (uint256 liquidationBonus) {\\n        // Retrieve liquidation bonus for the given token\\n        Liquidation memory liq = liquidationBonusForToken[token];\\n        unchecked {\\n            if (liq.bonusBP == 0) {\\n                // If there is no specific bonus for the token\\n                // Use default bonus\\n                liq.minBonusAmount = Constants.MINIMUM_AMOUNT;\\n                liq.bonusBP = dafaultLiquidationBonusBP;\\n            }\\n            liquidationBonus = (borrowedAmount * liq.bonusBP) / Constants.BP;\\n\\n            if (liquidationBonus < liq.minBonusAmount) {\\n                liquidationBonus = liq.minBonusAmount;\\n            }\\n            liquidationBonus *= (times > 0 ? times : 1);\\n        }\\n    }\\n```\\n\\nAs we can see, the liquidation bonus is based on the entire `borrowAmount` and multiplied by the number of new loans added. The problem is that it is unfair when the user makes a borrow against multiple lenders.\\nIf a user takes a borrow for X against 1 lender, they'll have to pay a liquidation bonus of Y. However, if they take a borrow for 3X against 3 lenders, they'll have to pay 9Y, meaning that taking a borrow against N lenders leads to overpaying liquidation bonus by N times.\\nFurthermore, if the user simply does it in multiple transactions, they can avoid these extra fees (as they can simply call `borrow` for X 3 times and pay 3Y in Liquidation bonuses)

Impact:
Loss of funds

Recommended mitigation:
make liquidation bonus simply a % of totalBorrowed
Vulnerability: When the amout of token acquired by a flash loan exceeds the expected value, the callback function will fail.
Severity: medium

Description:
When the amout of token acquired by a flash loan exceeds the expected value, the callback function will fail.\\nThe function `wagmiLeverageFlashCallback` is used to handle the repayment operation after flash loan. After obtaining enough saleToken, it uses `_v3SwapExact` to convert the saleToken into holdToken. We know that the amount of holdTokens (holdTokenAmtIn) is proportional to the amount of saleTokens (amountToPay) obtained from flash loans. Later, the function will check the `holdTokenAmtIn` is no large than decodedData.holdTokenDebt.\\n```\\n// Swap tokens to repay the flash loan\\nuint256 holdTokenAmtIn = _v3SwapExact(\\n    v3SwapExactParams({\\n        isExactInput: false,\\n        fee: decodedData.fee,\\n        tokenIn: decodedData.holdToken,\\n        tokenOut: decodedData.saleToken,\\n        amount: amountToPay\\n    })\\n);\\ndecodedData.holdTokenDebt -= decodedData.zeroForSaleToken\\n    ? decodedData.amounts.amount1\\n    : decodedData.amounts.amount0;\\n\\n// Check for strict route adherence, revert the transaction if conditions are not met\\n(decodedData.routes.strict && holdTokenAmtIn > decodedData.holdTokenDebt).revertError(\\n    ErrLib.ErrorCode.SWAP_AFTER_FLASH_LOAN_FAILED\\n);\\n```\\n\\nIn the function `_excuteCallback`, the amount of token finally obtained by the user through flash loan is `flashBalance`, which is the balance of the contract.\\n```\\n// Transfer the flashBalance to the recipient\\ndecodedData.saleToken.safeTransfer(decodedDataExt.recipient, flashBalance);\\n// Invoke the WagmiLeverage callback function with updated parameters\\nIWagmiLeverageFlashCallback(decodedDataExt.recipient).wagmiLeverageFlashCallback(\\n    flashBalance,\\n    interest,\\n    decodedDataExt.originData\\n);\\n```\\n\\nNow let me describe how the attacker compromises the flash loans.\\nFirst, the attacker makes a donation to the `FlashLoanAggregator` contract before the victim performs a flash loan (using front-run). Then victim performs a flash loan, and he/she will get much more flashBalance than expected. Finally, in the function `wagmiLeverageFlashCallback`, the holdTokenAmtIn is larger than experted, which leads to fail.

Impact:
DOS

Recommended mitigation:
In the function `_excuteCallback`, the amount of token finally obtained by the user through flash loan should be the the balance difference during the flash loan period.