Farseen0/scar-pairs huggingface dataset:
MEDIUM severity: Inconsistent CVI value handling during the deposit in `MegaThetaVault` contract. **Severity**: Medium

**Status**: Resolved

**Description**

The `depositForOwner` function in the `MegaThetaVault` contract takes the `_realTimeCVIValue` as a parameter and assigns it to balanceCVIValue. The function then checks if `_realTimeCVIValue` is less than balanceCVIValue. Since they are...

Vulnerable code:
function depositForOwner(address _owner, uint168 _tokenAmount, uint32 _realTimeCVIValue) external override returns (uint256 thetaTokensMinted) {
       require(msg.sender == fulfiller);


       uint32 balanceCVIValue = _realTimeCVIValue;
       if (_realTimeCVIValue < balanceCVIValue) {
           balanceCVIValue = _realTimeCVIValue;
       }
MEDIUM severity: Native coin sent can be lost. **Severity**: Medium

**Status**: Resolved

**Description**

In CPortModule.sol - Function `_executeOrderBuySide()` accepts native coin `msg.value` in a sale that requires ERC20 payment without reverting. Native coin in this scenario is transferred to the contract to be effectively lost. This...

Vulnerable code:
uint256 msgValueItemPrice = 0;

        if (saleDetails.paymentMethod == address(0)) {
            if (feeOnTop.amount + saleDetails.itemPrice != msgValue) {
                revert cPort__IncorrectFundsToCoverFeeOnTop();
            }
            
            msgValueItemPrice = saleDetails.itemPrice;
        }

        _fulfillSingleOrderWithFeeOnTop(
            disablePartialFill,
            msg.sender,
            saleDetails.maker,
            IERC20(saleDetails.paymentMethod),
            _getOrderFulfillmentFunctionPointers(saleDetails.paymentMethod, saleDetails.protocol),
            saleDetails,
            _validateBasicOrderDetails(msgValueItemPrice, saleDetails),
            feeOnTop);

function buyListingCosigned(
        bytes32 domainSeparator, 
        Order memory saleDetails, 
        SignatureECDSA memory sellerSignature,
        Cosignature memory cosignature
    ) public payable {
        _executeOrderBuySide(
            domainSeparator, 
            true,
            msg.value,
            saleDetails, 
            sellerSignature,
            cosignature,
            FeeOnTop({recipient: address(0), amount: 0})
        );
    }
HIGH severity: Price is fixed for any ERamount. **Severity**: Medium

**Status**: Resolved

**Description**

CPortModule.sol - In the scenario of listing ERC1155 via protocol ERC1155_FILL_PARTIAL, the taker pays the same price for any requestedAmount they demand of a given tokenId.

This is because of a bug in the following code...

Vulnerable code:
if (quantityToFill != saleDetails.amount) {
    saleDetails.amount = quantityToFill;
    saleDetails.itemPrice = saleDetails.itemPrice / saleDetails.amount * quantityToFill;
}
MEDIUM severity: No Price Staleness Check. **Severity** - Medium

**Status** - Acknowledged

**Description**

The function latestRoundData() has been used to fetch the price of an asset, but there are no price staleness checks.
Instances:
L24 -> AggregatedChainnlinkOracle.sol
L25 -> WBTCOracle.sol

There should be checks for the roundId...

Vulnerable code:
(uint80 roundID, int256 answer, , uint256 timestamp, uint80 answeredInRound) = _btcUsdFeed.latestRoundData();
require(answeredInRound >= roundID, "Stale price");
require(timestamp != 0,"Round not complete");
LOW severity: Missing sanity checks in `RewardAuctionHouse`. **Severity**: Low

**Status**:  Resolved

**Description**

There is Missing sanity checks for parameters in initialize() function. Here the minimumBid can be accidentally set to zero, or the auctionDuration can be zero as well as the beneficiary be set to zero address. This could lead to issues as.....

Vulnerable code:
minimumBid = minimumBid_;
        auctionDuration = auctionDuration_;
        beneficiary = beneficiary_;
HIGH severity: Tranche’s withdrawal fees are not deducted properly. **Severity**: High

**Status**: Acknowledged

**Description**

In Contract Tranche.sol, methods redeem/withdraw calls _withdraw(...) method internally. The method _withdraw has the following logic:

Here, the fee is calculated but the whole amount of `assets` is sent to the receiver address...

Vulnerable code:
function _withdraw(
       address caller,
       address receiver,
       address owner,
       uint256 assets,
       uint256 shares
   ) internal virtual override {
       require(
           utilizationRatio() < withdrawThreshold,
           "UTILIZATION_RATIO_MAX"
       );


       uint256 fee = getWithdrawalFeesRaw(assets);


       super._withdraw(caller, receiver, owner, assets, shares);


       if (fee > 0) {
           SafeERC20.safeTransfer(ERC20(asset()), address(vaultManager), fee);
           vaultManager.allocateRewards(fee);
       }
 … }

function testWithdrawalFee() public {
       uint rand = uint(keccak256(abi.encodePacked(block.timestamp))) %
           numTraders;
       address ownerTrader = traders[rand];
       vm.startPrank(ownerTrader);
       uint amountDeposited = usdc.balanceOf(ownerTrader);
       usdc.approve(address(juniorTranche), amountDeposited);


       emit log_named_uint("usdc amount deposited", amountDeposited);
       juniorTranche.deposit(amountDeposited, ownerTrader);


       emit log_named_uint(
           "minted shares",
           juniorTranche.balanceOf(ownerTrader)
       );


       uint withdrawalFeeRaw = juniorTranche.getWithdrawalFeesRaw(
           juniorTranche.balanceOf(ownerTrader)
       );
       emit log_named_uint("withdraw fee raw", withdrawalFeeRaw);


       emit log_named_uint(
           "withdraw fee total",
           juniorTranche.getWithdrawalFeesTotal(
               juniorTranche.balanceOf(ownerTrader)
           )
       );


       uint redeemB = juniorTranche.redeem(
           juniorTranche.balanceOf(ownerTrader),
           ownerTrader,
           ownerTrader
       );


       emit log_named_uint("redeem return assets", redeemB);


       emit log_named_uint(
           "usdc balance after redeem for owner",
           usdc.balanceOf(ownerTrader)
       );


       emit log_named_uint(
           "lp shares for owner",
           juniorTranche.balanceOf(ownerTrader)
       );


       assertEq(
           usdc.balanceOf(ownerTrader),
           amountDeposited - withdrawalFeeRaw
       );
   }

super._withdraw(caller, receiver, owner, assets-fee, shares);
MEDIUM severity: Method `getEarnings()` underflows. **Severity**: Medium

**Status**: Resolved

**Description**

As we notice in the above findings, the principalAssetDeposited is being increased for a receiver who has 0 LP tokens or no shares. The method getEarning(...) returns an unexpected result if it is checked for the same...

Vulnerable code:
function testGetEarnings() public {
       uint rand = uint(keccak256(abi.encodePacked(block.timestamp))) %
           numTraders;
       address ownerTrader = traders[rand];
       vm.startPrank(ownerTrader);
       uint amount = usdc.balanceOf(ownerTrader);
       usdc.approve(address(juniorTranche), amount);


       emit log_named_uint("usdc amount deposited", amount);
       juniorTranche.deposit(amount, ownerTrader);


       emit log_named_uint(
           "minted shares",
           juniorTranche.balanceOf(ownerTrader)
       );
       address receiverTrader = makeAddr("receiver");


       emit log_named_uint(
           "earning for receiver before redeem",
           uint256(juniorTranche.getEarnings(receiverTrader))
       );
       uint redeemB = juniorTranche.redeem(
           juniorTranche.balanceOf(ownerTrader),
           receiverTrader,
           ownerTrader
       );


       emit log_named_uint("redeem return assets", redeemB);
       emit log_named_uint(
           "usdc balance after redeem for receiver",
           usdc.balanceOf(receiverTrader)
       );


       emit log_named_uint(
           "lp shares for receiver",
           (juniorTranche.balanceOf(receiverTrader))
       );
       emit log_named_uint(
           "earning for receiver after redeem",
           uint256(juniorTranche.getEarnings(receiverTrader))
       );
   }
MEDIUM severity: Possibility of ETH being stuck in Trading contract. **Severity**: Medium

**Status**: Acknowledged

**Description**

In Contract Trading.sol, the methods updateSl(...) and updateTpAndSl(...) allows users to update the SL of an open trade. These methods internally call the _updateSl(...) method which has the following check.

In this logic,...

Vulnerable code:
if (
           _newSl == 0 ||
           !aggregator.pairsStorage().guaranteedSlEnabled(_pairIndex)
       ) {
           storageT.updateSl(msg.sender, _pairIndex, _index, _newSl);
           emit SlUpdated(
               msg.sender,
               _pairIndex,
               _index,
               _newSl,
               block.timestamp
           );
       } else {
		.
		.
		aggregator.fulfill{value: msg.value}(orderId, priceUpdateData);
	}
LOW severity: Function is returning the wrong first empty index. **Severity**: Low

**Status**: Resolved

**Description**

TradingStorage.sol - Function firstEmptyTradeIndex() mistakenly returns index = 0 since this is the default value to be returned. This takes place when the _openTrades array is filled as the loop surpasses maxTradesPerPair iterations and...

Vulnerable code:
function firstEmptyTradeIndex(address trader, uint pairIndex) public view override returns (uint index) {
        for (uint i = 0; i < maxTradesPerPair; i++) {
            if (_openTrades[trader][pairIndex][i].leverage == 0) {
                index = i;
                break;
            }
        }
    }

function firstEmptyOpenLimitIndex(address trader, uint pairIndex) public view override returns (uint index) {
        for (uint i = 0; i < maxTradesPerPair; i++) {
            if (!hasOpenLimitOrder(trader, pairIndex, i)) {
                index = i;
                break;
            }
        }
    }

252     require(
253         storageT.openTradesCount(msg.sender, t.pairIndex) +
254             storageT.pendingMarketOpenCount(msg.sender, t.pairIndex) +
255             storageT.openLimitOrdersCount(msg.sender, t.pairIndex) <
256             storageT.maxTradesPerPair(),
257         "MAX_TRADES_PER_PAIR"
258     );
LOW severity: Referrer codes of user can be overridden and lost. **Severity**: Low

**Status**: Resolved

**Description**

Referral.sol - In registerCode() the mapping codes[msg.sender] can be overridden by a new _code on each time the sender invokes this function.

The issue arises in functions setCodeOwner(), govSetCodeOwner() as well. It overrides any code...

Vulnerable code:
function registerCode(bytes32 _code) external {
        require(_code != bytes32(0), "ReferralStorage: invalid _code");
        require(codeOwners[_code] == address(0), "ReferralStorage: code already exists");

        codeOwners[_code] = msg.sender;
        codes[msg.sender] =  _code;
        referrerTiers[msg.sender] = _DEFAULT_TIER_ID;

        emit RegisterCode(msg.sender, _code);
    }

function setCodeOwner(bytes32 _code, address _newAccount) external {
        require(_code != bytes32(0), "ReferralStorage: invalid _code");

        address account = codeOwners[_code];
        require(msg.sender == account, "ReferralStorage: forbidden");

        codeOwners[_code] = _newAccount;

        delete codes[account];
        codes[_newAccount] = _code;

        emit SetCodeOwner(msg.sender, _newAccount, _code);
    }

    function govSetCodeOwner(bytes32 _code, address _newAccount) external override onlyGov {
        require(_code != bytes32(0), "ReferralStorage: invalid _code");

        address account = codeOwners[_code];
        delete codes[account];

        codeOwners[_code] = _newAccount;
        codes[_newAccount] = _code;

        emit GovSetCodeOwner(_code, _newAccount);
    }
HIGH severity: Unintended tokens proportion reset in `applySwapsAndPassiveRebal` function. **Severity**: Critical

**Status**:  Resolved

**Description**

The `applySwapsAndPassiveRebal` function, which is called in the deposit function of AFiBase contract, depending on certain conditions, invokes the applyRebalForProportions function in the AFiPassiveRebalanceStrategies contract. The...

Vulnerable code:
if (
     typeOfProduct == 2 &&
     isPassiveRebalEnabled &&
     (block.timestamp - lastRebalTime >= rebalPeriod) &&
     strategy >= 1
   ) {
     uTokenProportions = rebalContract.applyRebalForProportions(
       address(this),
       aFiManager,
       address(aFiStorage),
       strategy,
       uTokens
     );

uint[] memory uTokenProportions = new uint[](_tokens.length);
   if (_strategyNumber == 1) {
     uTokenProportions =  IAFiManager(_aFiManager).updateUTokenProportion(_aFiContract, _aFiStorage);
   }
   return uTokenProportions;
HIGH severity: Potential asset under-disbursement in withdraw function of AFiBase contract. **Severity**: High

**Status**:  Resolved

**Description**

The withdrawal function in the AFiBase contract is designed to allow users to redeem their shares for the corresponding assets. The function computes the user's entitled assets based on their share in the pool. Then, the...

Vulnerable code:
if (IERC20(oToken).balanceOf(address(this)) >= redFromContract) {
     IERC20(oToken).safeTransfer(msg.sender, redFromContract);
   } else {
     IERC20(oToken).safeTransfer(msg.sender, IERC20(oToken).balanceOf(address(this)));
   }
HIGH severity: Misleading slippage calculation. **Severity**: High

**Status**:  Resolved

**Description**

The uniswapV3Router function from AFiBase contract is intended to calculate and use the minimumAmountOut to set a floor on the acceptable amount of output tokens the caller will receive from a Uniswap swap, as a protection against...

Vulnerable code:
//safe calculation
   if (minimumAmountOut >= 10) {
     minimumAmountOut -= (minimumAmountOut - 10);
   }
HIGH severity: Update proportion not handling properly when totalProportion > 100%. **Severity**: High

**Status**: Resolved

**Description**

In Contract AFiManager.sol, the method updateProportion(...) is handling the case when totalProportion > 100% as follows:

In this case, there can be situations where this fix will fail:

1. If the last proportion is not more than...

Vulnerable code:
// fail safe condition
if(totalProp > 10000000){ 
uint256 rem = totalProp - 10000000;
if(_uTokenProportions[uTokenLen - 1] > rem){
_uTokenProportions[uTokenLen - 1] -= rem;
}
}
MEDIUM severity: Pool address can be address(0) and revert due to the same. **Severity**: Medium

**Status**: Resolved

**Description**

In Contract AFiStorage.sol, method getMinimumAmount(...) has the following check:

Here, if tokenIn == midTok, the getPool will return address(0) as both of the addresses are the same here. This will revert to an error with the message...

Vulnerable code:
else if(_tokenIn == (midTok) || _tokenOut == (midTok)){
address pool = IAFi(afiContract).getPool(_tokenIn, midTok); 
amountOut = IUniswapOracleV3(uniswapOracleV3).estimateAmountOutMin(
_tokenIn,
uint128(_amountIn),
midTok,
pool
);
}else {
LOW severity: AFiBase contracts can never be inactive once activated. **Severity**: Low

**Status**: Resolved

**Description**

In Contract AFiStorage.sol, the mapping isAFIActive can only be set true for any aFiBase contract. Since this can be set false once activated, all the `require statements`  checking if any aFiBase contract is active or not will become...

Vulnerable code:
updateRebalanceDetails(...)
addTeamWallet(...)
deactivateTeamWallet(...)
reactivateTeamWallet(...)
LOW severity: Unable to set stale price delay if initialized less than 1 hour. **Severity**: Low

**Status**: Resolved

**Description**

In Contract AFiOracle.sol, the method `intializeStalePriceDelay(...)` initializes the stale prices for tokens of an aFiContract without checking if it’s greater than 1 hour.

If any token’s stale price delay is set for less than an hour...

Vulnerable code:
require(stalePriceDelay[aFiContract][uToken] > 1 hours, "AFO01");

require(stalePriceDelay[aFiContract][uToken] > 0, "AFO01");
LOW severity: Unnecessary nested mapping. **Severity**: Low

**Status**: Acknowledged

**Description**

In Contract AFiStorage.sol, the variables aaveTokenCopy, compoundCopy, dTokenCopy are a mapping of type (address => (address => address)).

Here, the first key is the aFiBase Contract address, the second key is the uToken address and...

Vulnerable code:
aaveTokenCopy[0xaa…aa1][0xbb..bb1] = 0xcc..cc1
aaveTokenCopy[0xaa…aa1][0xbb..bb2] = 0xcc..cc2
aaveTokenCopy[0xaa…aa2][0xbb..bb1] = 0xcc..cc1
aaveTokenCopy[0xaa…aa2][0xbb..bb2] = 0xcc..cc2
HIGH severity: Accounting discrepancy due to fee-on-transfer token. **Severity**: High

**Status**: Resolved

**Description**

The YolloToken contract extends the basic functionality of ERC20 with additional features, such as fee deduction on transfers, and implements the ERC1363 standard. To purchase a package, a user needs to transfer the staking token to the...

Vulnerable code:
function test_onTransferReceivedConsiderTokenFeeWhenUpdatingStakingTokenTransfersForUsers()
       public
   {
       uint256 stakingContractBalanceBefore = yollloToken.balanceOf(
           address(staker)
       );
       vm.startPrank(user1);


       yollloToken.transferAndCall(address(staker), stakeAmount);


       uint256 stakingContractBalanceAfter = yollloToken.balanceOf(
           address(staker)
       );


       assertEq(
           staker.stakingTokenTransfers(user1),
           stakingContractBalanceAfter - stakingContractBalanceBefore
       );
   }
MEDIUM severity: Accrue package reward does not transfer enough tokens for rewards. **Severity**: Medium

**Status**: Acknowledged

**Description**

In Contract `YollloStaking.sol`, the method `accruePackageReward(...)` allows the package manager role to accrue rewards for the user and transfer the rewards from the treasury wallet to the staking contract.

In this logic, fees on...

Vulnerable code:
function test_accruePackageRewardMonthlyShouldSendCorrectAmountOfTokensToStakingContract()
       public
   {
       stakingTokenTransferForPuchasingPackage();
       puchasingPackageWithStakingTokenTransfer();


       vm.startPrank(packageManager);


       uint256 stakingContractBalanceBefore = yollloToken.balanceOf(
           address(staker)
       );


       for (uint i = 0; i < annualAccrualCount; i++) {
           vm.warp(block.timestamp + (i + 1) * 30 days + 1);
           staker.accruePackageReward(user1, 1);
       }


       uint256 stakingContractBalanceAfter = yollloToken.balanceOf(
           address(staker)
       );


       assertEq(
           staker.getPackage(user1, 1).rewards,
           stakingContractBalanceAfter - stakingContractBalanceBefore
       );
       vm.stopPrank();
   }
LOW severity: Tiers can be set with principal USD as 0. **Severity**: Low

**Status**: Resolved

**Description**

In Contract YollloStaking.sol, the method `setTier(...)` allows the admin to set a new Tier with principalUSD as 0. If such a tier is set, the method getTier() and getTiers() will not include those in the tiers list for the following...

Vulnerable code:
if (_tiers[i].principalUSD > 0) {
               tiers[currentTierCount++] = _tiers[i];
           }

if (tier.principalUSD == 0) {
           revert TierNotFound(tierNumber);
       }

function test_RevertsWhenPuchasingPackageWithTierSetWithZeroPrincipalUSD()
       public
   {
       staker.setTier(2, 0, 100, 100);


       stakingTokenTransferForPuchasingPackage();
       vm.startPrank(packageManager);
       vm.expectRevert(abi.encodeWithSelector(TierNotFound.selector, 2));


       staker.purchasePackage(user1, 2, stakeAmount, true);
   }
LOW severity: Package’s `currentMPR` can be 0. **Severity**: Low

**Status**: Resolved

**Description**

In Contract YollloStaking.sol, the method `purchasePackage(...)` allows to purchase a package and sets the currentMPR for a package using the following formula:

Here if tier.passiveAPR is less than 12 which is highly unlikely will set the...

Vulnerable code:
package.currentMPR = tier.passiveAPR / annualAccrualCount;

uint256 currentMPR = apr / annualAccrualCount;

function test_CurrentMPR_is_Zero_WhenNewPackageIsPuchased() public {
       staker.setTier(2, 10000, 10, 10); // passiveAPR < 12
       stakingTokenTransferForPuchasingPackage();


       vm.startPrank(packageManager);
       staker.purchasePackage(user1, 2, stakeAmount, true);


       uint256 currentMPR = staker.getPackage(user1, 2).currentMPR;
       assertEq(currentMPR, 0);
       vm.stopPrank();
   }
MEDIUM severity: Signature is replayable. **Severity**: Medium

**Status**: Resolved

**Description**

WalletAccount.sol - Function `validateUserOp()` (i.e. in BaseAccount.sol) is susceptible to replay attacks. A rogue caller is able because of this vulnerability to reuse an already executed signature to authorize a new action without the.....

Vulnerable code:
function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds)
    external override virtual returns (uint256 validationData) {
44  _requireFromEntryPoint();
45  validationData = _validateSignature(userOp, userOpHash);
46  _validateNonce(userOp.nonce);

function _validateAndUpdateNonce(address sender, uint256 nonce) internal returns (bool) {

        uint192 key = uint192(nonce >> 64);
        uint64 seq = uint64(nonce);
        return nonceSequenceNumber[sender][key]++ == seq;
    }
LOW severity: Lack of event emission. **Severity**: Low

**Status**: Resolved

**Description**

WalletAccount.sol - Function `withdrawDepositTo()` is an important function that is called by privileged admin in order to withdraw valuable assets.

This function should emit event to help the stakeholders in the project to follow up with...

Vulnerable code:
function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public onlyOwner {
        entryPoint().withdrawTo(withdrawAddress, amount);
    }
LOW severity: No 0 address check in ‘withdrawDepositTo’ function. **Severity**: Low

**Status**: Resolved

**Location**: WalletAccount.sol

**Description**

`withdrawDepositTo` function in the provided code allows the withdrawal of an amount to a specified address. However, there is no check to ensure that the withdrawAddress is not the zero address (0x0)....

Vulnerable code:
require(withdrawAddress != address(0), "withdrawDepositTo: withdraw to the zero address");
MEDIUM severity: Wrong `avgDepositPrice` calculated. **Severity**: Medium

**Status**: Resolved

**Description**

In Contract VaultETH_V2, the method Request_BuyETH(...) allows users to buy ETH using ZeUSD tokens.

In the method, the epoch’s average deposit price is calculated as follows:

`tempEpochInfo.avgDepositPrice` is the weighted average ETH...

Vulnerable code:
tempEpochInfo.avgDepositPrice =
           (tempEpochInfo.rmDeposit *
               tempEpochInfo.avgDepositPrice +
               sendAmt *
               ethPrice) /
           (tempEpochInfo.rmDeposit + sendAmt);

       *// Update the total deposit Ether amount for the epoch*
       tempEpochInfo.rmDeposit += ethAmt;

tempEpochInfo.avgDepositPrice = (tempEpochInfo.rmDeposit * tempEpochInfo.avgDepositPrice + ethAmt * ethPrice) / (tempEpochInfo.rmDeposit + ethAmt);
MEDIUM severity: Require check in `MultiSig_V2` can be bypassed. **Severity**: Medium

**Status**:  Resolved

**Description**

In the MultiSig contract, the require check statement of `_numConfirmationsRequired <= _whiteWallet.length` could be bypassed if `DeregisterWhiteWallet()` is used. For example, if the wallet `_whiteWallet.length` is 3 and the...

Vulnerable code:
require(
            _numConfirmationsRequired > 0 &&
                _numConfirmationsRequired <= _whiteWallet.length,
            "invalid number of required confirmations"
        );