Farseen0/scar-pairs huggingface dataset:
LOW severity: `doNFTTransferIn()` should not perform fee on transfer token check. In `doNFTTransferIn()`, we perform a check that is taken from the ERC20 market, checking the balance before and after the transfer, and adjusting the amount to the difference between these values.

While this situation should not be possible (the number of NFTs transferred should always equal the...

Vulnerable code:
function doNFTTransferIn(address from, uint[] memory nftIds) virtual internal returns (uint) {
    // Read from storage once
    IERC721 underlying_ = IERC721(underlying);
    uint balanceBefore = underlying_.balanceOf(address(this));

    for(uint i = 0; i < nftIds.length;) {
        underlying_.transferFrom(from, address(this), nftIds[i]);
        heldNFTs.push(nftIds[i]);
        unchecked { i++; }
    }

    // Calculate the amount that was *actually* transferred
    uint balanceAfter = underlying_.balanceOf(address(this));
    return balanceAfter - balanceBefore;   // underflow already checked above, just subtract
}
LOW severity: Domain separator of FUNG token will temporarily be set to 0 before initialization. In the FUNG token's `initialize()` function, we set the `DOMAIN_SEPARATOR` as follows:

This value is then used in permit signatures to ensure they were intended to be applied in this context.

However, there is no check that the DOMAIN_SEPARATOR has been set before it is used. This means that, in.....

Vulnerable code:
DOMAIN_SEPARATOR = keccak256(
    abi.encode(
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
        keccak256(bytes("Fung Token")),
        keccak256(bytes("1")),
        block.chainid,
        address(this)
    )
);

function permit(
    address owner,
    address spender,
    uint256 value,
    uint256 deadline,
    uint8 v,
    bytes32 r,
bytes32 s
) external {
    if (deadline < block.timestamp) {
        revert ExpiredPermit();
    }
+   if (DOMAIN_SEPARATOR == bytes32(0)) {
+       revert UninitializedDomainSeparator();
+   }
    bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))));
    address recoveredAddress = ecrecover(digest, v, r, s);
    if (recoveredAddress == address(0) || recoveredAddress != owner) {
        revert InvalidPermitSignature();
    }
    _approve(owner, spender, value);
}
LOW severity: Unchecked math in price oracle is not guaranteed to be safe. In the Chainlink Price Oracle, we expect the price of the asset to be returned in 8 decimals. We then perform the following calculation to transfer it into `36 - asset decimals` decimals:

While this multiplication is likely to be safe in most practical cases, it is not guaranteed to be safe.

-...

Vulnerable code:
unchecked { price = uint(rate) * 10**(28 - decimals[asset]); }
HIGH severity: [I-03] ERcallback isn't used. In `CErc721.sol`, we include the `onERC721Received()` function, which allows NFTs to be received only if they are sent by the contract itself.

However, there is only one place where NFTs are transferred into the contract, and that is in the `doNFTTransferIn()` function:

This function uses the...

Vulnerable code:
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external override returns (bytes4) {
    if (operator != address(this)) {
        revert Unauthorized();
    }
    return IERC721Receiver.onERC721Received.selector;
}

function doNFTTransferIn(address from, uint[] memory nftIds) virtual internal returns (uint) {
    // Read from storage once
    IERC721 underlying_ = IERC721(underlying);
    uint balanceBefore = underlying_.balanceOf(address(this));

    for(uint i = 0; i < nftIds.length;) {
        underlying_.transferFrom(from, address(this), nftIds[i]);
        heldNFTs.push(nftIds[i]);
        unchecked { i++; }
    }

    ...
}
HIGH severity: UniV3 Oracle unsafe on s in event of Sequencer downtime. The UniV3 oracle uses the built in `consult()` function provided by Uniswap's Oracle Library to query the pool and determine the time weighted price. This takes in a `secondsAgo` and observes the price at `secondsAgo` and `block.timestamp`, returning the time weighted average between these two...

Vulnerable code:
function getSurroundingObservations(
        Observation[65535] storage self,
        uint32 time,
        uint32 target,
        int24 tick,
        uint16 index,
        uint128 liquidity,
        uint16 cardinality
    ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
        // optimistically set before to the newest observation
        beforeOrAt = self[index];

        // if the target is chronologically at or after the newest observation, we can early return
        if (lte(time, beforeOrAt.blockTimestamp, target)) {
            if (beforeOrAt.blockTimestamp == target) {
                // if newest observation equals target, we're in the same block, so we can ignore atOrAfter
                return (beforeOrAt, atOrAfter);
            } else {
                // otherwise, we need to transform
                return (beforeOrAt, transform(beforeOrAt, target, tick, liquidity));
            }
        }
        ...
}
MEDIUM severity: Tokens with extreme price differences along the path could round price to zero. In the unlikely event that the price ratio between two tokens is more than `1e18:1`, the oracle will round the price down zero.

While the largest price ratio I can currently find is `BTC/SHIB` (which can be calculated using...

Vulnerable code:
function _getQuoteAmount(QuoteParams calldata quoteParams_) internal view returns (uint256) {
    ...
    if (pd.inverted) price = WAD.divWadDown(price);
+   if (price == 0) revert ZeroPrice();
    return _convertPriceToQuoteAmount(price, quoteParams_);
}
LOW severity: Rearrange stale price check formula out of absurd paranoia. In `_getFeedAnswer()`, we check if the Chainlink oracle has returned a stale price:

`feed_.staleAfter` is a `uint24`, so for any chain that uses standard Unix timestamps, it should be impossible for `block.timestamp - feed_.staleAfter` to underflow (because the current Unix time is greater than...

Vulnerable code:
if (updatedAt < block.timestamp - feed_.staleAfter) {
    revert StalePrice(feed_.feed, updatedAt);
}

- if (updatedAt < block.timestamp - feed_.staleAfter) {
+ if (updatedAt + feed_.staleAfter < block.timestamp) {
      revert StalePrice(feed_.feed, updatedAt);
  }
HIGH severity: ThrottledExit contract is affected by Out-of-gas error, because of usage of cycle to calculate nextAvailableExitBlock in case if current mined block is far in future comparing to initial nextAvailableExitBlock.. **Recommendation**:
Instead of doing iterations it is better to use mathematical formula to calculate
nextAvailableExitBlock as

Vulnerable code:
nextAvailableExitBlock = nextAvailableExitBlock + ((block.number - nextAvailableExitBlock) /
throttleRoundBlocks + 1) * throttleRoundBlocks
HIGH severity: Method massUpdatePools from CrossPool contract is vulnerable to `Out of gas`. **Description**

Method massUpdatePools from CrossPool contract is iterating over all registered pools, and if
the number of pools exceeds a specific number, then it will fail execution. It also affects the
addition of new pools by using the method `add` and passing arguments withUpdate equal
to...

Vulnerable code:
uint256 length = poolInfo.length;
for (uint256 pid = 0; pid < length; ++pid) {
updatePool(pid);
}
HIGH severity: CrossPool staker can get fewer reward tokens in case if RewardVault has less balance than accumulated reward tokens. **Description**

RewardVault is not returning the actual amount of tokens transferred to Stacker and
CrossPool contract marks that all reward tokens transferred.

**Snippet 1**:

**Snippet 2**:

Vulnerable code:
if(block.number >= timeLock && totalPending > 0) {
user.accClaim = 0;
rewardVault.safeRewardTransfer(msg.sender, totalPending);
}

function safeRewardTransfer(
address to,
uint256 amount
) public onlyOwner {
uint256 rewardTokenBalance = rewardToken.balanceOf(address(this));
uint256 transferableAmount = amount > rewardTokenBalance
? rewardTokenBalance
: amount;
rewardToken.safeTransfer(to, transferableAmount);
}
LOW severity: Method initialize from CrossPool contract utilizes misleading names for arguments blockDuration & timeLockDuration. **Description**
Arguments blockDuration & timeLockDuration are used as a number of blocks instead of
commonly used to represent a number of seconds.

**Snippet**:

Vulnerable code:
startBlock = startBlock_;
endBlock = startBlock_.add(blockDuration);
timeLock = startBlock_.add(timeLockDuration);
HIGH severity: Contract owner can transfer tokens allocated for investors. **Description**

Contract owner can invoke the smart contract PrivateDistributions’s method recoverToken
and pass argument _token as UnmarshalToken address to withdraw tokens allocated for
investors.

**Snippet**:

**Recommendation**:

Forbid to pass argument _token equal to UnmarshalToken address.

Vulnerable code:
function recoverToken(address _token, uint256 amount) external onlyOwner {
IERC20(_token).safeTransfer(_msgSender(), amount);
emit RecoverToken(_token, amount);
}
HIGH severity: Method releaseTokens has multiple issues. **Description**

Method releaseTokens of contract PrivateDistribution, that is invocable only by the contract
owner, has set of issues at the moment:
- it releases tokens for first 256 registered investors, because of uint8 overflow, if the
number of investors is greater than 256;
- it does not...

Vulnerable code:
function releaseTokens() external onlyOwner initialized() {
for (uint8 i = 0; i < investors.length; i++) {
uint256 availableTokens = withdrawableTokens(investors[i]);
_oddzToken.safeTransfer(investors[i], availableTokens);
}
}
HIGH severity: Investors will not be able to withdraw tokens after the end of the vesting duration for Ecosystem in UnmarshalTokenVesting. **Description**

Array ecosystemVesting length is equal to 50. If more than 50 months have passed since the
initial time function _calculateAvailablePercentage will lead to error.

**Snippet**:

**Recommendation**:

Add check for noOfMonths > 50, if it evaluates to true, then returns last value...

Vulnerable code:
if (currentTimeStamp > _initialTimestamp) {
if (distributionType == DistributionType.ECOSYSTEM) {
return ecosystemVesting[noOfMonths];
HIGH severity: Investors will not be able to withdraw tokens after the end of the vesting duration for STAKING, MARKETING and RESERVES in UnmarshalTokenVesting. **Description**

Array marketingReserveVesting length is equal to 38. If more than 38 months have passed
since the initial time function _calculateAvailablePercentage will lead to error.

**Snippet**:

**Recommendation**:

Add check for noOfMonths > 38 if true return last value of...

Vulnerable code:
if (currentTimeStamp > _initialTimestamp) {
if (distributionType == DistributionType.ECOSYSTEM) {
return ecosystemVesting[noOfMonths];
} else if (
distributionType == DistributionType.MARKETING ||
distributionType == DistributionType.RESERVES ||
distributionType == DistributionType.STAKING
) {
return marketingReserveVesting[noOfMonths];
HIGH severity: Incorrect withdraw logic for team distribution type. **Description**

Function _calculateAvailablePercentage for team distribution will always return 100% if current
timestamp is before of initial cliff (240 days).

**Snippet**:

**Recommendation**:

Extend `else` block with the condition to check if the current timestamp is greater than...

Vulnerable code:
if (currentTimeStamp > initialCliff && currentTimeStamp < vestingDuration) {
uint256 noOfDays = BokkyPooBahsDateTimeLibrary.diffDays(initialCliff, currentTimeStamp);
if (noOfDays == 1) {
return uint256(15).mul(1e18);
} else if (noOfDays > 1) {
uint256 currentUnlockedPercentage = noOfDays.mul(everyDayReleasePercentage);
// console.log("Current Unlock %: %s", currentUnlockedPercentage);
return uint256(15).add(currentUnlockedPercentage);
}
} else {
return uint256(100).mul(1e18);
}
HIGH severity: PrivateDistribution does not guarantee the allocation of tokens for investors. **Description**

Method _addInvestor does not allocate tokens for investors during their creation and requires
the governance address manager to transfer tokens manually to PrivateDistribution contract
address.

**Snippet**:

**Recommendation**:

Invoke transferFrom to method _addInvestor on the...

Vulnerable code:
function _addInvestor(
address _investor,
uint256 _tokensAllotment,
uint256 _allocationType
) internal onlyOwner {
require(_investor != address(0), "Invalid address");
require(_tokensAllotment > 0, "the investor allocation must be more than 0");
Investor storage investor = investorsInfo[_investor];
require(investor.tokensAllotment == 0, "investor already added");
investor.tokensAllotment = _tokensAllotment;
investor.exists = true;
investors.push(_investor);
investor.allocationType = AllocationType(_allocationType);
_totalAllocatedAmount = _totalAllocatedAmount.add(_tokensAllotment);
emit InvestorAdded(_investor, _msgSender(), _tokensAllotment);
}
HIGH severity: Owner can set initial timestamp several times in UnmarshalTokenVesting. **Description**

Function setInitialTimestamp does not change the value of isInitialized to true.

**Snippet**:

**Recommendation**:

Remove comment.

Vulnerable code:
function setInitialTimestamp(uint256 _timestamp) external onlyOwner() notInitialized() {
// isInitialized = true;
_initialTimestamp = _timestamp;
MEDIUM severity: Require function can't throw exceptions in UnmarshalTokenVesting. **Description**

Function _addDistribution is internal. All distributions are declared when deploying the
contract including tokens allotment and beneficiary address, so require can’t throw exceptions
for _tokensAllotment > 0 and distribution.tokensAllotment ==...

Vulnerable code:
require(_tokensAllotment > 0, "the investor allocation must be more than 0");
require(distribution.tokensAllotment == 0, "investor already added");
LOW severity: Extra debug events are present in PrivateDistribution smart contract. **Description**

PrivateDistribution utilizes library hardhat/console.sol for debugging events.

**Snippet**:

**Recommendation**:
Remove debug events.

Vulnerable code:
console.log("Withdrawable Tokens: %s", tokensAvailable);
console.log("Everyday Percentage: %s, Days: %s, Current Unlock %: %s",everyDayReleasePercentage,
noOfDays, currentUnlockedPercentage);
MEDIUM severity: The useless if and SafeMath omission:. **Description**

The if statement is unnecessary here and the omission of SafeMath may lead to the overflow.

**Recommendation**:

Remove the if statement and implement proper SafeMath calls.

Vulnerable code:
function calculatePendingRewards(address to) public view virtual override whenNotPaused returns (uint256
pendingRewards){
// Get the current Block
uint256 _currentBlock = block.number;
// Get the time in number of blocks
uint256 _rewardBlock = _currentBlock.sub(_stakedBlocks[to], "STokens: Error in subtraction");
// Get the balance of the account
uint256 _balance = balanceOf(to);
// Calculate the interest if P, R, T are non zero values

return pendingRewards;
}
if(_balance > 0 && _rewardRate > 0 && _rewardBlock > 0) {
pendingRewards = (_balance * _rewardRate * _rewardBlock) / 100;
}
HIGH severity: Evergrowing array:. **Description**

![image](https://github.com/user-attachments/assets/22799dde-9c63-45cb-a343-838121160417)

The problem with this loop is that on every unStake() the user pushes a new unstake value to
the array increasing its length, but on withdrawUnstakedTokens() array’s length is not
decreased......

Vulnerable code:
mapping(address => uint256) internal _counters;
function withdrawUnstakedTokens() {
...
for (uint256 i = ; i < _unstakingExpiration[staker].length; i++) {
if (block.timestamp > _unstakingExpiration[staker][i]) {
_withdrawBalance =_withdrawBalance +
_unstakingAmount[staker][i];
_unstakingExpiration[staker][i] = 0;
_unstakingAmount[staker][i] = 0;
_counters[_msgSender()]++;
}
}
...
}
LOW severity: Useless parameter variable:. **Description**

**Recommendation**:
The from variable can be omitted and _msgSender() used instead.

Vulnerable code:
function withdrawUTokens( , uint256 tokens, string memory
toAtomAddress) public virtual override whenNotPaused {
require(tokens>0, "TokenWrapper: Number of unstaked tokens should
be greater than 0");
uint256 _currentUTokenBalance = _uTokens.balanceOf(from);
require(_currentUTokenBalance>=tokens, "TokenWrapper: Insuffcient
balance for account");
require(from == _msgSender(), "TokenWrapper: Withdraw can only be done by Staker");
_uTokens.burn(from, tokens);
emit WithdrawUTokens(from, tokens, toAtomAddress,
block.timestamp);
}
LOW severity: _calculateDiscount function could be a little bit more gas efficient. **Description**

The function is in contract ArgoPayments.sol (argo-payments-contract-master), three
modification can save some gas:
Function takes parameter a, then assigns its value to a new variable amount. I believe
function parameter can be simply called amount at the first place.
Loop in the.....

Vulnerable code:
function _calculateDiscount(address u, uint256 amount) internal view
returns (uint256)
{
if (!discountsEnabled) return 0;
uint256 stake = stakingManager.balanceOf(u, address(underlying));
uint256 percent = 0;
uint256 slabLength = discountSlabs.length;
for (uint256 i = 0; i < slabLength; i++) {
if (stake >= discountSlabs[i].amount) {
percent = discountSlabs[i].percent;
} else {
break;
}
}
return (amount * percent * PRECISION) / PERCENT;//discount
}
HIGH severity: Function “buyBack” can be called by anyone. **Description**

At contract PrimaryPoolERC20.sol function buyBack allows to buy back tokens for any user.
This will work if there is permission on erc20 token transfer. Although tokens can’t be stolen
this way, but it may lead to unwanted “buy backs”.

**Recommendation**:

Unless there is a...

Vulnerable code:
function buyBack(address destination, uint256 tokensAmount) public {
require(destination != address(0),"Destination address is empty");
...
_collateralization.buyBack(destination, tokensAmount, collateralAmount);
emit BuyBack(tokensAmount, blackAndWhitePrice);
}

function buyBack(uint256 tokensAmount) public {
...
_collateralization.buyBack(msg.sender, tokensAmount, collateralAmount);
emit BuyBack(tokensAmount, blackAndWhitePrice);
}
HIGH severity: Unnecessary variables declaration. **Description**

Although the current code looks very good, it can be a little bit more gas efficient. At contract
PrimaryPoolERC20.sol there is a function getBWprice() that creates several variables.
We can try to do some optimization, to save gas fees. It is important to keep the function...

Vulnerable code:
function getBWprice() public view returns (uint256) {
// Get data from collateralization contract
uint256 curSupply = _collateralization.getWhiteSupply().add(_collateralization.getBlackSupply());
/*
Calculate token price. If current collateralization is less than default price use default price.
If token collateralization is higher than default price define new price from collateralization.
*/
if (curSupply == 0) {
return _blackAndWhitePrice;
}
uint256 newPrice = wdiv(_collateralization.getCollateralization(), curSupply);
return (newPrice > _blackAndWhitePrice) ? newPrice : _blackAndWhitePrice;
}
LOW severity: Gas efficiency could be improved. **Description**

At contract Incentives.sol, putIncentivesTokens function there is a loop that constantly checks
allTokens array length:

Also, this check doesn’t seem to make sense:

Since the amount already been checked at line 130:

So the else statement will always be...

Vulnerable code:
for(uint256 i=0;i<allTokens.length;i++)

if(amount<users[msg.sender].borrowedList[tokenAddress]){
users[msg.sender].borrowedList[tokenAddress] =
users[msg.sender].borrowedList[tokenAddress].sub(amount);
}else{
users[msg.sender].borrowedList[tokenAddress] = 0;
}

require(amount>0 && users[msg.sender].borrowedList[tokenAddress] >= amount,"bad amount or
tokenAddress");
LOW severity: **Misleading event**. **Description**

In `SecondaryPoolBWT.sol` file, there are two misleading events:

**Recommendation**:

Depending on requirements, switch tokens or events. For example, in case of tokens: set black on
line 429 and white on line 470.

Vulnerable code:
emit BlackPriceCase1(eend.whitePrice); // line 429
emit WhitePriceCase1(eend.blackPrice); // line 470
MEDIUM severity: Transfer of busd to same recipient is unnecessarily repeated in loops. **Severity**: Medium

**Status**: Resolved

**Description**

dpnm_sc.sol - busd.safeTransfer(feeCollector, ...) is repeated in loop in both methods: _TreePayment and depositFordPNMBuy, safeTransfer is a costly operation, and it can be called only once on the total amount to be transferred, rather...

Vulnerable code:
uint256 amountToBeTransferred;

for (uint i=0; i <10; i++)  
   amountToBeTransferred += amountToBeAdded
busd.safeTransfer(feeCollector, amountToBeTransferred);
MEDIUM severity: Unchecked return. **Severity**: Medium

**Status**: Resolved

**Description**

dpnm_sc.sol - In buyTurnoverWithGWT(uint) & buyEarnLimitWithGWT(uint) , the return value from the burning operation of gwt is not validated gwt.burn(msg.sender, gwtCost).
In buydPNM(uint) & selldPNM(uint) , the return value from the...

Vulnerable code:
require(gwt.burn(msg.sender, gwtCost), ...).
require(gwt.mint(msg.sender, gwtCost), ...).
LOW severity: No validation applied on the constructor arguments. **Severity**: Low

**Status**: Resolved

**Description**

No require statements to validate inputs of constructor during deployment.

**Recommendation**: 
Add require statements similar to the following:

Vulnerable code:
require(address(voidToken) != address(0));
require(minimumStakeableAmount >  0);
require(bonusEndBlock > startBlock )
require(blockTime > 0);
require(rewardPerBlock > 0);
HIGH severity: Cast issue for negative value. **Severity**: High

**Status**: Resolved

**Description**

In contract VaultFeeManager, function _getVaultRebalanceFees, the userPositionDelta is cast to uint256. If the userPositionDelta is negative, it will be cast to a very large positive number, and this could lead to unexpected behavior.  The.....

Vulnerable code:
uint256 lockedBalanceSansUserDelta = userPositionDelta > 0
              ? currentBalance - uint256(userPositionDelta)
               : currentBalance + uint256(-userPositionDelta);
MEDIUM severity: Possibility of incorrect minting of liquidity via finalize(). **Severity**: Medium

**Status**: Resolved

**Description**

Depending upon the logic of TangleswapCallingParams.mintParams(which is out of scope of this audit) if the parameters-
        uint160 sqrtPriceX96,
        int24 tickLower,
        int24 tickUpper
- to the finalize() function are...

Vulnerable code:
uint160 sqrtPriceX96,
int24 tickLower,
int24 tickUpper
LOW severity: Possibility of pre-distribution balance manipulation.. **Severity**: Low

**Status**: Resolved

**Description**

The  startDistribution function  in ERC20Distribution and  ERC20DistributionNative contracts is designed to begin the distribution of Gain Token to users. Function ensures that the amount of Gain Token held by the contract matches the total.....

Vulnerable code:
require(
       _trusted_token.balanceOf(address(this)) >=_total_distribution_balance,
       'Initial distribution balance must be correct'
       );
LOW severity: Lack of protection against user’s excessive ether loss. **Severity**: Low

**Status**: Resolved

**Description**

The ERC20DistributionNative.purchaseTokens checks whether the transaction ether value is sufficient to purchase the required amount of Gain Token. If the transaction value is higher than the required amount, the excess ether will be sent to.....

Vulnerable code:
require(
       msg.value == fiattoken_amount,
       "PurchaseTokens: transaction value must be sufficient"
     );