Farseen0/scar-pairs huggingface dataset:
HIGH severity: RuggedArt — In the function targetedPurchase(), an user could give an arbitrary swapParam which includes the field commands to 4. It then calls the UNIVERSAL_ROUTER.execute() function.  According to the Uniswap Technical Reference, commands 4 is SWEEP and it will call the sweep() function. As pointed from the following figure, the sweep() function will send back ETH to the user contract and cause a re-entrancy issue.. https://twitter.com/AnciliaInc/status/1760718167582888148

Vulnerable code:
function _executeSwap(UniversalRouterExecute calldata swapParam) private {
    if (msg.value > 0) {
        UNIVERSAL_ROUTER.execute{value: msg.value}(
            swapParam.commands,
            swapParam.inputs,
            swapParam.deadline
        );
    } else {
        revert InvalidParameter();
    }
}

// If a user want to use ETH to buy Rug token, then it will use Uniswap to swap first,
// user will use Uniwap UNIVERSAL_ROUTER to complete this step, _executeSwap function will be called
function targetedPurchase(
    uint256[] memory _tokenIds,
    UniversalRouterExecute calldata swapParam
) public payable {
    uint256 beforeSwapBalance = ruggedToken.balanceOf(address(this));
    _executeSwap(swapParam);
    uint256 afterSwapBalance = ruggedToken.balanceOf(address(this));
    uint256 totalAmount = afterSwapBalance - beforeSwapBalance;
    if (totalAmount < _tokenIds.length * 1.1 ether) {
        revert InvalidAmount();
    }
    _targetedPurchase(_tokenIds);
} ... /// @notice Sweeps all of the contract's ERC20 or ETH to an address
/// @param token The token to sweep (can be ETH using Constants.ETH)
/// @param recipient The address that will receive payment
/// @param amountMinimum The minimum desired amount
function sweep(address token, address recipient, uint256 amountMinimum) internal {
    uint256 balance;
    if (token == Constants.ETH) {
        balance = address(this).balance;
        if (balance < amountMinimum) revert InsufficientETH();
        if (balance > 0) recipient.safeTransferETH(balance);
    } else {
        balance = ERC20(token).balanceOf(address(this));
        if (balance < amountMinimum) revert InsufficientToken();
        if (balance > 0) ERC20(token).safeTransfer(recipient, balance);
    }
}

Vulnerability type: Reentrancy
HIGH severity: CitadelFinance — The vulnerability in the redeem() function of the Redemption smart contract allowed the attacker to manipulate the WETH/USDC.E pool price on Camelot. This caused the getAmountsOut() function to overestimate the value of deposited LP tokens, resulting in inflated redemption amounts.. https://medium.com/neptune-mutual/how-was-citadel-finance-exploited-a5f9acd0b408

Vulnerable code:
function redeem(uint256 underlying, uint256 token, uint256 amount, uint8 rate) public nonReentrant {
    require(underlying == 0 || underlying == 1, "Invalid underlying");
    require(token == 0 || token == 1, "Invalid token");
    require(rate == 0 || rate == 1, "Invalid rate");
    require(amount > 0, "Amount must be greater than 0");

    uint256 amountAvailable = CITStaking.redeemCalculator(msg.sender);

        path;

            path[0] = address(USDC); // 1e6
            path[1] = address(WETH); // 1e18

            uint[] memory a = camelotRouter.getAmountsOut(_amount / 1e12, path); // result in 18 decimal

            amountInUnderlying = a[1];
        } else {
            amountInUnderlying = _amount / 1e12; // 1e6 is the decimals of USDC, so 18 - 12 = 6
        }
    }

    if (token == 0) {
        CIT.burn(CITStakingAddy, amount);
        CITStaking.removeStaking(msg.sender, address(CIT), rate, amount);
    } else if (token == 1) {
        totalbCITRedeemedByUser[msg.sender] += amount;
        bCIT.burn(CITStakingAddy, amount);
        CITStaking.removeStaking(msg.sender, address(bCIT), rate, amount);
    }

    treasury.distributeRedeem(tokenAddy, amountInUnderlying, msg.sender);
}

Vulnerability type: Price Manipulation, Reentrancy
HIGH severity: ShibaToken — The `balanceOf` function of `Shiba` is not changed, so it will return all balances(including the `locked` amount). This is not a best practice. User could `transferLockToken` or `batchTransferLockToken` to the pair address. And the pair itself could not identify if the token is locked or not. As a result, the swap would still succeed. And the user could sell the `locked` token.. https://app.blocksec.com/explorer/tx/bsc/0x75a26224da9faf37c2b3a4a634a096af7fec561f631a02c93e11e4a19d159477

Vulnerable code:
function transferLockToken(address _wallet, uint256 _amount) public {
    users[_wallet].lockedBalance = users[_wallet].lockedBalance.add(_amount);
    users[_wallet].unlockPerSecond = users[_wallet].lockedBalance.mul(unlockPercent).div(100).div(duration);

    super.transfer(_wallet, _amount);
}

function batchTransferLockToken(Airdrop[] memory _airdrops) public {
    for (uint256 i = 0; i < _airdrops.length; i++) {
        // don't use this.transferTokenLock because payer modifier
        address wallet = _airdrops[i].wallet;
        uint256 amount = _airdrops[i].amount;

        users[wallet].lockedBalance = users[wallet].lockedBalance.add(amount);
        users[wallet].unlockPerSecond = users[wallet].lockedBalance.mul(unlockPercent).div(100).div(duration);

        super.transfer(wallet, amount);
    }
}

function transfer(address _to, uint256 _amount) public override returns (bool) {
    uint256 availableAmount = getAvailableBalance(_msgSender());
    require(availableAmount >= _amount, "Not Enough Available Token");

    return super.transfer(_to, _amount);
}

Vulnerability type: ERC20
HIGH severity: LinkDAO — This project is similar to Uniswap V2 and has a swap function. At the end of the function, there should be a correct `K` verification. In Uniswap V2, which takes a 0.3% protocol fee, the verification is as follows:

```solidity
uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
```
It seems the victim contract takes a 0.03% protocol fee, but the verification is incorrect; it should multiply by (10000**2) instead of (1000**2).. [BlockSec Phalcon on Twitter / X](https://x.com/phalcon_xyz/status/1725058908144746992)

Vulnerable code:
uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');

Vulnerability type: Dex/AMM, Incorrect logic
HIGH severity: VeloCore — Lacks permission verification (velocore__execute). The LP Pool lacks permission verification. The attacker directly invoke the velocore__execute function (0xec378808) of the LP contract with a carefully constructed parameter to manipulate the feeMultiplier parameter of the contract.. The LP Pool lacks permission verification. The attacker directly invoke the velocore__execute function (0xec378808) of the LP contract with a carefully constructed parameter to manipulate the feeMultiplier parameter of the contract.

Vulnerable code:
function velocore_execute(address, Token[] calldata tokens, int128[] memory r, bytes calldata data) external returns (int128[] memory, int128[] memory) {
    if (data.length > 0) {
        _return_logarithmic_swap();
    }
    uint256 effectiveFee9 = fee9;
    if (lastWithdrawTimestamp == block.timestamp) {
        unchecked {
            effectiveFee9 = effectiveFee9 * feeMultiplier / 1e9;
        }
    }
} ... if (iLp != type(uint256).max && r.u(iLp) > 0) {
    _simulateBurn(uint256(int256(r.u(iLp))));
    if (lastWithdrawTimestamp != block.timestamp) {
        feeMultiplier = 1e9;
        lastWithdrawTimestamp = uint32(block.timestamp);
    }
    feeMultiplier = (feeMultiplier * invariantMax / (invariantMax - uint256(int256(r.u(iLp)))).toUint128();
} else if (iLp != type(uint256).max && r.u(iLp) < 0) {
    _simulateMint(uint256(int256(-r.u(iLp))));
}
return (new int128[](tokens.length), r);

Vulnerability type: Access Control, Lack of permission control
HIGH severity: ARK — Business Logic Flaw, Hacker can destroy the ARK Token in the trading pair, causing the price of ARK Token in the pair to increase. Finally, the attacker exchanges a small amount of ARK Token. https://twitter.com/Phalcon_xyz/status/1771728823534375249

Vulnerable code:
function autoBurnLiquidityPairTokens() public {
    lastLpBurnTime = block.timestamp;
    uint256 liquidityPairBalance = balanceOf(_mainPair);
    uint256 amountToBurn = (liquidityPairBalance * percentForLPBurn) / 10000;
    if (amountToBurn > 0) {
        _basicTransfer(_mainPair, address(0xdead), amountToBurn);
    }
    ISwapPair(_mainPair).sync();
    emit AutoNukeLP();
}

Vulnerability type: Business Logic Flaw, Reentrancy
HIGH severity: Sentiment — Attacker re enter Balancer Vaults execute malicious code before pool balances were updated and steal money using overpriced collateral. the `_joinOrExit` function is called whenever the `joinPool` or `exitPool` is called by an external protocol user.. https://twitter.com/peckshield/status/1643417467879059456 https://twitter.com/spreekaway/status/1643313471180644360 https://medium.com/coinmonks/theoretical-practical-balancer-and-read-only-reentrancy-part-1-d6a21792066c https://forum.balancer.fi/t/reentrancy-vulnerability-scope-expanded/4345

Vulnerable code:
function joinPool(
        bytes32 poolId,
        address sender,
        address recipient,
        JoinPoolRequest memory request
    ) external payable override whenNotPaused {
        // This function doesn't have the nonReentrant modifier: it is applied to `_joinOrExit` instead.

        // Note that `recipient` is not actually payable in the context of a join - we cast it because we handle both
        // joins and exits at once.
        _joinOrExit(PoolBalanceChangeKind.JOIN, poolId, sender, payable(recipient), _toPoolBalanceChange(request)); //vulnerable point
    }

function _joinOrExit(
        PoolBalanceChangeKind kind,
        bytes32 poolId,
        address sender,
        address payable recipient,
        PoolBalanceChange memory change
    ) private nonReentrant withRegisteredPool(poolId) authenticateFor(sender) {  //vulnerable point
        // This function uses a large number of stack variables (poolId, sender and recipient, balances, amounts, fees,
        // etc.), which leads to 'stack too deep' issues. It relies on private functions with seemingly arbitrary
        // interfaces to work around this limitation.

        InputHelpers.ensureInputLengthMatch(change.assets.length, change.limits.length);

        // We first check that the caller passed the Pool's registered tokens in the correct order, and retrieve the
        // current balance for each.
        IERC20[] memory tokens = _translateToIERC20(change.assets);
        bytes32[] memory balances = _validateTokensAndGetBalances(poolId, tokens);
        ...

Vulnerability type: Reentrancy
HIGH severity: SQUID — In the victim contract, there are functions for swapping old tokens for new ones and vice versa. The "sellSwappedTokens" function performs the swap using PancakeSwap. The hacker exploited this by sandwiching the exchange, resulting in a gain of $87k.. https://twitter.com/0xNickLFranklin/status/1777235767577964980

Vulnerable code:
function sellSwappedTokens(uint256 sellOption) external nonReentrant lock {
    require(swapEnabled, "Swap is not enabled yet");
    uint256 sellAmount;
    // uint256 sellAmount = totalSwappedToSell > 500000 ether ? 500000 ether : totalSwappedToSell;
    if (sellOption == 1) {
        sellAmount = totalSwappedToSell > ALTERNATIVE_SELL_AMOUNT ? ALTERNATIVE_SELL_AMOUNT : totalSwappedToSell;
    } else {
        sellAmount = totalSwappedToSell > DEFAULT_SELL_AMOUNT ? DEFAULT_SELL_AMOUNT : totalSwappedToSell;
    }

    require(sellAmount > 0, "No tokens to sell");

    uint256 squidV2BalanceBefore = newSquidToken.balanceOf(address(this));

    // Set slippage to 5%
    uint256 minOut = getMinOut(sellAmount);

    // Approve the router to spend SQUID V1
    oldSquidToken.approve(address(pancakeRouter), sellAmount);

    address;
    path[0] = address(oldSquidToken);
    path[1] = addressWBNB;
    path[2] = address(newSquidToken);

    pancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(
        sellAmount,
        minOut, // Min amount out after slippage
        path,
        address(this),
        block.timestamp
    );

    // Update totalSwappedToSell
    totalSwappedToSell -= sellAmount;

    // Calculate the amount of new SQUID V2 to burn
    uint256 newSquidBalance = newSquidToken.balanceOf(address(this));

    uint256 burnSquidV2Amount = newSquidBalance - squidV2BalanceBefore;
    // Assuming SQUID V2 has a burn function or sending to a dead address
    // newSquidToken.burn(newSquidBalance); // Implement if burn function exists
    // newSquidToken.transfer(0x000000000000000000000000000000000000dEaD, burnSquidV2Amount); // Send to dead address to "burn"

    if (burnSquidV2Amount > 0) {
        // Burn the SQUID V2 tokens by transferring to a dead address
        newSquidToken.transfer(0x000000000000000000000000000000000000dEaD, burnSquidV2Amount);
    }
}

Vulnerability type: Sandwich
HIGH severity: OverNight — Flashloan - force investment. https://twitter.com/peckshield/status/1598704809690877952 https://twitter.com/overnight_fi/status/1572614168976691200 https://medium.com/coinmonks/psa-in-crypto-transparency-doesnt-necessarily-equal-safety-86b23ae9f471

Vulnerable code:
https://snowtrace.io/address/0xed2a7edd7413021d440b09d654f3b87712abab66#code#F24#L731

Vulnerability type: Flashloans
HIGH severity: ATM — A flawed transfer implementation leads to the attack. The root cause was using a swap to convert BNB to BUSDT for dividend payments, allowing the attacker to perform a sandwich attack. The attacker repeatedly transferred ATM tokens to the PancakeSwap pool and called the skim function, exploiting the bad implementation. As you can see, there is no slippage check in the swap operation.. [Nick L. Franklin on Twitter / X](https://twitter.com/0xNickLFranklin/status/1775008489569718508)

Vulnerable code:
function swapTokensForCurrency(uint256 tokenAmount) private {
    address;
    path[0] = address(this);
    path[1] = currency;

    _approve(address(this), address(_swapRouter), tokenAmount);

    // make the swap
    try
        _swapRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(
            tokenAmount,
            0,
            path,
            address(_tokenDistributor),
            block.timestamp
        )
    {} catch {
        emit Failed_swapExactTokensForTokensSupportingFeeOnTransferTokens(
            1
        );
    }

    uint256 currencyBal = IERC20(currency).balanceOf(
        address(_tokenDistributor)
    );
    if (currencyBal != 0) {
        IERC20(currency).transferFrom(
            address(_tokenDistributor),
            address(this),
            currencyBal
        );
    }
}

Vulnerability type: Flashloans, Slippage
HIGH severity: Transit Swap — Incorrect owner address validation. You can input any innocent user who granted approvals to `0xed1afc8c4604958c2f38a3408fa63b32e737c428` before. 2.Contract `0xed1afc8c4604958c2f38a3408fa63b32e737c428` will perform transferFrom to transfer amount of innocent user to attacker. `function`: `claimTokens(address token,address from,address to,uint256 amount)`,`params`: [`0x55d398326f99059fF775485246999027B3197955`,`0x1aAe0303f795b6FCb185ea9526Aa0549963319Fc`,`0x75F2abA6a44580D7be2C4e42885D4a1917bFFD46`, `6312858905558909501615`. 1.You can input any innocent user who granted approvals to "0xed1afc8c4604958c2f38a3408fa63b32e737c428" before. 2.Contract "0xed1afc8c4604958c2f38a3408fa63b32e737c428" will perform transferFrom to transfer amount of innocent user to attacker. 3. 
"function": "claimTokens(address token,address from,a

Vulnerable code:
function claimTokens(address varg0, address varg1, address varg2, uint256 varg3) public payable {
    require(msg.data.length - 4 >= 128);

    require(varg0 == varg1);
    // No owner address validation
    require(varg2 == varg2);

    v0 = v1 = msg.sender == _claimTokens;

    if (msg.sender != _claimTokens) {
        v0 = 0xff & owner_3[msg.sender];
    }
    require(v0, 'XswapApprove: Access restricted');
    if (varg3) {
        MEM[36 + MEM[64] + 32] = varg2;
        MEM[36 + MEM[64] + 64] = varg3; // transferFrom
        MEM[MEM[64] + 32] = varg1 & 0xffffffffffffffffffffffffffffffffffffffffffffffff | 0x23b872dde0e0...;
        v2 = v3 = 0;
        while (v2 < 100) {
            MEM[v2 + MEM[64]] = MEM[32 + (MEM[64] + v2)];
            v2 += 32;
        }
    }
}

Vulnerability type: Dex/AMM, Insufficient validation
HIGH severity: GDS — Abused the LP Mining mechanism.  The attacker abused the LP Mining mechanism of the GDS token by first adding liquidity with flashloan, and then using multiple contracts to collect rewards.  GDS token will reward the users who add liquidity to (GDS, USDT) pair in each epoch and the reward amount lpRewardAmount = x * LP token holding amount / LP token total supply (x is a global variable). Thus, the more staking amount, the more rewards users can get.  Borrowing 2.38M USDT token with flashloan.  Swapping 0.6M USDT token for 3.4M GDS token.  Adding liquidity for PancakeSwap (1.7M USDT and 3.4M GDS) and get 2.2M LP tokens.  Collecting rewards from GDS token contract and transferring LP tokens to another new contract.  Repeating step 4 for more than 70 times.  Removing liquidity and repaying the flashloan.  1.Borrowing 2.38M USDT token with flashloan.  1. Swapping 0.6M USDT token for 3.4M GDS token. 2. Adding liquidity for PancakeSwap (1.7M USDT and 3.4M GDS) and get 2.2M LP tokens. 3. Collecting rewards from GDS token contract and transferring LP tokens to another new contract.  5.Repeating step 4 for more than 70 times.  6.Removing liquidity and repaying the flashloan.  1.Borrowing 2.38M USDT token with flashloan.  1. Swapping 0.6M USDT token for 3.4M GDS token. 2. Adding liquidity for PancakeSwap (1.7M USDT and 3.4M GDS) and get 2.2M LP tokens. 3. Collecting rewards from GDS token contract and transferring LP tokens to another new contract.  5.Repeating step 4 for more than 70 times.  6.Removing liquidity and repaying the flashloan.. https://twitter.com/peckshield/status/1610095490368180224 https://twitter.com/BlockSecTeam/status/1610167174978760704

Vulnerable code:
function _settlementLpMining(address _from) internal {
    uint256 _lpTokenBalance = IERC20(gdsUsdtPair).balanceOf(_from);
    uint256 _lpTokenTotalSupply = IERC20(gdsUsdtPair).totalSupply();
    if (lastEpoch[_from] > 0 && currentEpoch > lastEpoch[_from] && _lpTokenBalance > 0) {
        uint256 _totalRewardAmount = 0;
        for (uint i = lastEpoch[_from]; i < currentEpoch; i++) {
            _totalRewardAmount += everyEpochLpReward[i];
            _totalRewardAmount += everyDayLpMiningAmount;
        }

        uint256 _lpRewardAmount = _totalRewardAmount * _lpTokenBalance / _lpTokenTotalSupply;
        _internalTransfer(lpPoolContract, _from, _lpRewardAmount, 4);

        lastEpoch[_from] = currentEpoch;
    }

    if (lastEpoch[_from] == 0 && _lpTokenBalance > 0) {
        lastEpoch[_from] = currentEpoch;
    }

    if (_lpTokenBalance == 0) {
        lastEpoch[_from] = 0;
    }
}

Vulnerability type: Flashloans, staking
HIGH severity: JokInTheBox — Hacker could unstake several times because `unstake` function doesn't check `unstaked` value.. https://x.com/0xNickLFranklin/status/1800355604692910571

Vulnerable code:
function unstake(uint256 stakeIndex) external {
    require(stakeIndex < stakes[msg.sender].length, "Invalid stake index!");
    Stake memory currentStake = stakes[msg.sender][stakeIndex];

    uint256 currentDay = getCurrentDay();

    require(currentDay > currentStake.stakedDay + currentStake.lockPeriod, "Lock period has not finalized!");
    stakes[msg.sender][stakeIndex].unstaked = true; // <-- vulnerable point
    stakes[msg.sender][stakeIndex].unstakedDay = currentDay;

    totalStaked -= currentStake.amountStaked;
    // Transfer back staked amount
    require(jokToken.transfer(msg.sender, currentStake.amountStaked), "Token transfer failed!");

    emit Unstake(msg.sender, currentStake.amountStaked, block.timestamp, currentStake.lockPeriod, stakeIndex);
}

Vulnerability type: Business Logic Flaw
HIGH severity: SheepFarm — The root cause of the attack is that the function `register` can be repeatedly invoked by the same account. 1. The attacker repeatedly invokes the `register` function to increase the GEM bonus   2.  The attacker invokes `addGems` and `upgradeVillage`, to update GEM value and accumulate yield  1. The attacker invokes `sellVillage` to convert GEM to money 2. Then withdraw the money(BNB) via `withdrawMoney`. 1. The attacker repeatedly invokes the `register` function to increase the GEM bonus 2.  The attacker invokes `addGems` and `upgradeVillage`, to update GEM value and accumulate yield 1. The attacker invokes `sellVillage` to convert GEM to money 2. Then withdraw the money(BNB) via `withdrawMoney`

Vulnerable code:
function register(address neighbor) external initialized {
    address user = msg.sender;
    require(villages[user].timestamp == 0, "just new users"); //@audit not updated while the new user creation
    uint256 gems;
    totalVillages++;
    if (villages[neighbor].sheeps[0] > 0 && neighbor != manager) {
        gems += GEM_BONUS * 2;
    } else {
        neighbor = manager;
        gems += GEM_BONUS;
    }
    villages[neighbor].neighbors++;
    villages[user].neighbor = neighbor;
    villages[user].gems += gems;
    emit Newbie(msg.sender, gems);
}

Vulnerability type: Incorrect logic
HIGH severity: LaunchZone — Contract X mentioned above is the implementation contract of SwapX Proxy (not yet verified and audited), the Proxy will allow users to pass data and call the implementation contract:. https://blog.verichains.io/p/analyzing-the-lz-token-hack https://twitter.com/immunefi/status/1630210901360951296

Vulnerable code:
function swap(
        IERC20 srcToken,
        IERC20 dstToken,
        address dstReceiver,
        uint256 amount,
        uint256 minReturnAmount,
        address referrer,
        bytes calldata data
    ) 
        external 
        payable 
        whenNotPaused
        returns (uint256 returnAmount)
    {
        require(minReturnAmount > 0, "Min return should not be 0");
        require(data.length > 0, "Call data should exist");

        dstReceiver = (dstReceiver == address(0)) ? msg.sender : dstReceiver;
        uint256 initialSrcBalance = srcToken.universalBalanceOf(msg.sender);
        uint256 initialDstBalance = dstToken.universalBalanceOf(dstReceiver);

        {
        (bool success, bytes memory returndata) = swapXImpl.call{value: msg.value}(data); //vulnerable point
        if (!success)  {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert("swap failed");
            }
        }
        }

Vulnerability type: Access Control, Price Manipulation
HIGH severity: HundredFinance — cToken.redeem() is rounded down - first deposit bug. https://twitter.com/peckshield/status/1647307128267476992 https://twitter.com/danielvf/status/1647329491788677121 https://twitter.com/hexagate_/status/1647334970258608131 https://blog.hundred.finance/15-04-23-hundred-finance-hack-post-mortem-d895b618cf33

Vulnerable code:
function redeemFresh(address redeemer, uint redeemTokensIn, uint redeemAmountIn) internal returns (uint) {
	...
	else
		(vars.mathErr, vars.redeemTokens) = divScalarByExpTruncate(redeemAmountIn, Exp(vars.exchangeRateMantissa));  // rounded down from 1.9999 -> 1 (should be rounded up to 2)

	uint256 allowed = comptroller.redeemAllowed(address(this), redeemer, vars.redeemTokens);
}

Vulnerability type: Donate, Math, Under/Overflow