Farseen0/scar-pairs huggingface dataset:
HIGH severity: Funds can be stolen after wrong approve.. **Severity**: Critical	

**Status**: Resolved

**Description**

The `refund()`, `withdrawFees()` and  `distributeWithoutFee()` functions within the `Treasury.sol` smart contract are used for transferring funds out from the Treasury to a receiver address. For this purpose, the functions implement a.....

Vulnerable code:
function refund(
       uint256 amount,
       address to
   ) public onlyRole(DISTRIBUTOR_ROLE) {
       IERC20(approvedToken).approve(to, amount);
       SafeERC20.safeTransfer(
           IERC20(approvedToken),
           to,
           amount * 10 ** IERC20Mint(approvedToken).decimals()
       );
   }

   /**
    * Withrad earned fees
    * @param amount amount to withdraw
    */
   function withdrawFees(
       uint256 amount,
       address to
   ) public onlyRole(DEFAULT_ADMIN_ROLE) {
       IERC20(approvedToken).approve(to, amount);
       SafeERC20.safeTransfer(IERC20(approvedToken), to, amount);
   }

function distributeWithoutFee(
       uint256 rate,
       address to,
       uint256 initialDeposit
   ) public onlyRole(DISTRIBUTOR_ROLE) {
       initialDeposit *= 10 ** IERC20Mint(approvedToken).decimals();
       uint256 withdrawnFees = (initialDeposit * fee) / FEE_DENOMINATOR;
       uint256 wonAmount = (initialDeposit - withdrawnFees) +
           ((initialDeposit - withdrawnFees) * rate) /
           FEE_DENOMINATOR;
       IERC20(approvedToken).approve(to, wonAmount);
       SafeERC20.safeTransfer(IERC20(approvedToken), to, wonAmount);
       if (getRakebackAmount(to, initialDeposit) != 0) {
           earnedRakeback[to] += getRakebackAmount(to, initialDeposit);
       }
   }
HIGH severity: Lock of fund due to the precision loss. **Severity**: Critical

**Status**: Resolved

**Description**

In the finalizeGame() function of the Bullseye contract, if there are 2 players, then the deposited amount is divided with such rates (75/25 or 80/20).

wonAmountFirst and wonAmountSecond is calculated by multiplying the ratio into the.....

Vulnerable code:
uint256 wonAmountFirst = (2 *
      game.depositAmount *
      (
           playerOneDiff <= exactRange
                ? twoPlayersExactRate[0]
                : twoPlayersRate[0]
      )) / DENOMINATOR;
ITreasury(treasury).distribute(
      wonAmountFirst,
      playerOne,
      game.depositAmount,
      fee
);
uint256 wonAmountSecond = (2 *
      game.depositAmount *
      (
          playerOneDiff <= exactRange
                 ? twoPlayersExactRate[1]
                 : twoPlayersRate[1]
       )) / DENOMINATOR;
ITreasury(treasury).distribute(
      wonAmountSecond,
      playerTwo,
      game.depositAmount,
      fee
);
HIGH severity: withdrawRakeback() function will always be failed. **Severity**: Critical

**Status**: Resolved

**Description**

In the withdrawRakeback() function of the Treasury contract, it mints xyro tokens to the users. The current IERC20Mint interface has a mint() function which returns a boolean but the xyro token implementation’s mint() function doesn’t...

Vulnerable code:
function withdrawRakeback(uint256 amount) public {
    require(
        earnedRakeback[msg.sender] >= amount,
        "Amount is greated than earned rakeback"
    );
    earnedRakeback[msg.sender] -= amount;
    IERC20Mint(xyroToken).mint(msg.sender, amount);
}



interface IERC20Mint {
    function decimals() external view returns (uint256);
    function mint(address to, uint256 value) external returns (bool);
}
HIGH severity: `stopLoss` and` takeProfit` can be set in the same direction, leading to `stopLoss` voters be always the winners.. **Severity**: Critical	

**Status**: Resolved

**Description**

The function `createSetup()` within the `Setup.sol` smart contract is used to create new Setup games. This function takes, `isLong`, `takeProfitPrice` and `stopLossPrice` as function parameters. For a correct behavior:

If `isLong` is.....

Vulnerable code:
if (isLong) {
           require(
               uint192(startingPrice) / 1e14 > stopLossPrice || 

                   uint192(startingPrice) / 1e14 < takeProfitPrice,
               "Wrong tp or sl price"
           );
       } else {
           require(
               uint192(startingPrice) / 1e14 < stopLossPrice ||
                   uint192(startingPrice) / 1e14 > takeProfitPrice,
               "Wrong tp or sl price"
           );
       }
HIGH severity: Users Would Lose Rewards If A Huge Majority Bets On The Winning Side Due To Rounding. **Severity** - High

**Status** - Resolved

**Description**

Consider the following scenario for a UpDown Game (winning side is down side) →

totalDepositsDown = 100 tokens

totalDepositsUp = 100000000 tokens

Now when the up down game gets finalised the following snippet executes to calculate the.....

Vulnerable code:
} else {
            uint256 finalRate = ITreasury(treasury).calculateUpDownRate(
                _game.totalDepositsUp,
                _game.totalDepositsDown,
                fee
            );

function calculateUpDownRate(
        uint256 lostTeamTotal,
        uint256 wonTeamTotal,
        uint256 updownFee
    ) external returns (uint256 rate) {
        lostTeamTotal *= 10 ** IERC20Mint(approvedToken).decimals();
        wonTeamTotal *= 10 ** IERC20Mint(approvedToken).decimals();
        uint256 lostTeamFee = (lostTeamTotal * updownFee) / FEE_DENOMINATOR;
        uint256 wonTeamFee = (wonTeamTotal * updownFee) / FEE_DENOMINATOR;
        collectedFee += lostTeamFee + wonTeamFee;
        //collect dust
        rate =
            ((lostTeamTotal - lostTeamFee) * FEE_DENOMINATOR) /
            (wonTeamTotal - wonTeamFee);
Plugging in the values , lostTeamTotal = 100 tokens and wonTeamTotal = 100000000 tokens we get rate to  be 0 due to rounding . 

Now , since rate is calculated as 0 when the rewards are distributed to the winning side this snippet would be triggered →

 for (uint i = 0; i < DownPlayers.length; i++) {
                ITreasury(treasury).distributeWithoutFee(
                    finalRate,
                    DownPlayers[i],
                    depositAmounts[DownPlayers[i]]
                );


function distributeWithoutFee(
        uint256 rate,
        address to,
        uint256 initialDeposit
    ) public onlyRole(DISTRIBUTOR_ROLE) {
        initialDeposit *= 10 ** IERC20Mint(approvedToken).decimals();
        uint256 withdrawnFees = (initialDeposit * fee) / FEE_DENOMINATOR;
        uint256 wonAmount = (initialDeposit - withdrawnFees) +
            ((initialDeposit - withdrawnFees) * rate) /
            FEE_DENOMINATOR;
        IERC20(approvedToken).approve(to, wonAmount);
        SafeERC20.safeTransfer(IERC20(approvedToken), to, wonAmount);
        if (getRakebackAmount(to, initialDeposit) != 0) {
            earnedRakeback[to] += getRakebackAmount(to, initialDeposit);
        }
HIGH severity: Finalizing a game after 10 minutes will lock user’s earned funds.. **Severity**: High	

**Status**: Resolved

**Description**

The `finalizeGame()` function within the `UpDown.sol` and `OneVsOneExactPrice`, smart contract is used by the administrator to end a game, submit the results, and distribute funds to the winners.
This function implements a `require` that...

Vulnerable code:
require(
           priceTimestamp - game.endTime <= 10 minutes || 

            block.timestamp - priceTimestamp <= 10 minutes, 

           "Old chainlink report"
       );
HIGH severity: One side players have more probability of winning than the other.. **Severity**: High	

**Status**: Resolved

**Description**

In the `UpDown` game, players can bet that an asset’s price will be up or down compared to the current price. If the price is up, players that voted for it will win and vice versa. However, the current implementation of the...

Vulnerable code:
if (uint192(finalPrice / 1e14) > _game.startingPrice) { // @audit why / 1e14 ahora?
           uint256 finalRate = ITreasury(treasury).calculateUpDownRate(
               _game.totalDepositsDown,
               _game.totalDepositsUp,
               fee
           );
           for (uint i = 0; i < UpPlayers.length; i++) {
               ITreasury(treasury).distributeWithoutFee(
                   finalRate,
                   UpPlayers[i],
                   depositAmounts[UpPlayers[i]]
               );
           }
           emit UpDownFinalized(finalPrice, true, currentGameId);
       } else {
           uint256 finalRate = ITreasury(treasury).calculateUpDownRate(
               _game.totalDepositsUp,
               _game.totalDepositsDown,
               fee
           );
           for (uint i = 0; i < DownPlayers.length; i++) {
               ITreasury(treasury).distributeWithoutFee(
                   finalRate,
                   DownPlayers[i],
                   depositAmounts[DownPlayers[i]]
               );
           }
           emit UpDownFinalized(finalPrice, false, currentGameId);
       }
HIGH severity: The opponent has a higher probability of winning than the initiator of the game.. **Severity**: High	

**Status**: Resolved

**Description**

The `OneVsOneExactPrice.sol` smart contract allows creating games where the exact result of an asset should be guessed. The game is played by the initiator and an opponent. The one who guess the closest price to the final price is the...

Vulnerable code:
uint256 diff1 = game.initiatorPrice > uint192(finalPrice) / 1e14 
           ? game.initiatorPrice - uint192(finalPrice) / 1e14
           : uint192(finalPrice) / 1e14 - game.initiatorPrice;
       uint256 diff2 = game.opponentPrice > uint192(finalPrice) / 1e14 
           ? game.opponentPrice - uint192(finalPrice) / 1e14
           : uint192(finalPrice) / 1e14 - game.opponentPrice;
       if (diff1 < diff2) { 
           ITreasury(treasury).distribute(
               game.depositAmount * 2,
               game.initiator,
               game.depositAmount,
               fee
           );
           emit ExactPriceFinalized(
               gameId,
               game.initiatorPrice,
               game.opponentPrice,
               finalPrice,
               Status.Finished
           );
       } else {
           ITreasury(treasury).distribute(
               game.depositAmount * 2,
               game.opponent,
               game.depositAmount,
               fee
           );
           emit ExactPriceFinalized(
               gameId,
               game.opponentPrice,
               game.initiatorPrice,
               finalPrice,
               Status.Finished
           );
       }
MEDIUM severity: Attacker Can Steal Initiator Fees. **Severity** - Medium

**Status** - Resolved

**Description**

Let’s consider the following scenario →

      2.) The attacker sees this tx in the mempool and backruns this (in the same block so that block.timestamp is same) providing the same params , this results in the same gameID and would...

Vulnerable code:
Victim creates a setup game(createSetup())  , the gameId is keccak256(
            abi.encodePacked(
                block.timestamp,
                endTime,
                takeProfitPrice,
                stopLossPrice
            )
        );

newGame.isLong = isLong;
        newGame.initiator = msg.sender;
        newGame.endTime = endTime;
        newGame.stopLossPrice = stopLossPrice;
        newGame.takeProfitPrice = takeProfitPrice;
        newGame.gameStatus = Status.Created;
        newGame.feedId = feedId;
        games[gameId] = newGame;
 And the new initiator would be the attacker.
MEDIUM severity: `DISTRIBUTOR` AND `DEFAULT_ADMIN` ROLES CAN DRAIN THE WHOLE TREASURY. **Severity**: Medium	

**Status**: Resolved

**Description**

The `Treasury.sol` smart contract implements a `refund()` and a `withdrawFees()` functions:

As it can be observed, both functions get an `amount` and a `to` parameters indicating the amount to be withdrawn and the receiver address,...

Vulnerable code:
/**
    * Refunds tokens
    * @param amount token amount
    * @param to reciever address
    */
   function refund(uint256 amount, address to) onlyRole(DISTRIBUTOR_ROLE) public {        IERC20(approvedToken).approve(to, amount);        SafeERC20.safeTransfer(IERC20(approvedToken), to, amount);
   }

   /**
    * Withrad earned fees
    * @param amount amount to withdraw
    */
   function withdrawFees(uint256 amount, address to) onlyRole(DEFAULT_ADMIN_ROLE) public {        IERC20(approvedToken).approve(to, amount);       SafeERC20.safeTransfer(IERC20(approvedToken), to, amount);
   }
MEDIUM severity: Playing is restricted for an unlimited amount of users. **Severity**: Medium	

**Status**: Resolved

**Description**

In order to take part in an `UpDown` game, a player must execute the `play()` or `playWithPermit()` functions within the `UpDown.sol` smart contract. These 2 functions implements an `isParticipating` modifier:

As it can be observed,...

Vulnerable code:
/**
    * Checks if player is participating in the game
    * @param player player address
    */
   modifier isParticipating(address player) {
       for (uint i = 0; i < UpPlayers.length; i++) { 
           require(UpPlayers[i] != player, "Already participating");
       }
       for (uint i = 0; i < DownPlayers.length; i++) {
           require(DownPlayers[i] != player, "Already participating");
       }
       _;
   }
MEDIUM severity: `endTime` can be set higher than `stopPredictAt` leading to a wrong behavior of the protocol.. **Severity**: Medium	

**Status**: Resolved

**Description**

When a new `UpDown` game is started, the admin must execute the `startGame` function within the `UpDown.sol` smart contract. This function receives several parameters, `endTime` and `stopPredictAt` among others.

`stopPredictAt`:...

Vulnerable code:
function startGame(
       uint48 endTime,
       uint48 stopPredictAt,
       bytes32 feedId
   ) public onlyRole(DEFAULT_ADMIN_ROLE) {
MEDIUM severity: Calculated commission cut does not align with the expected percentages. **Severity**: Medium	

**Status**: Resolved

**Description**

The `getComissionCut()` function within the `Treasury.sol` smart contract is used to calculate the commission that must be taken from the players. After some calculations for the ‘tier’, the commission is calculated. However, the...

Vulnerable code:
if (tier == 4) {
           //10-20%
           comissionCut = 3000;
       } else if (tier > 0) {
           //30%
           comissionCut = 1000 + 500 * tier - 1;
       }
MEDIUM severity: Games can get closed without a reason, leading to players not being paid. **Severity**: Medium	

**Status**: Acknowledged

**Description**

The `closeGame()` function within the `UpDown.sol` smart contract allows the administrator to close the game at any point in time without any reason, refunding users but not paying winners.

The same issue is present within the...

Vulnerable code:
require(
           ((data.startTime + (data.endTime - data.startTime) / 3 <
               block.timestamp &&
               (games[gameId].teamSL.length == 0 ||
                   games[gameId].teamTP.length == 0)) ||
               block.timestamp > data.endTime),
           "Wrong status!"
       );
MEDIUM severity: `startTime` is not checked, allowing games with past prices to be started.. **Severity**: Medium	

**Status**: Resolved

**Description**

The function `createSetup()` within the `Setup.sol` smart contract is used for starting a new set up game. This function receives an `unverifiedReport` parameter which is used to determine the startingPrice of the game. The problem is...

Vulnerable code:
(newGame.startringPrice, newGame.startTime) = IDataStreamsVerifier(            ITreasury(treasury).upkeep()
       ).verifyReportWithTimestamp(unverifiedReport, feedId);

(int192 startingPrice, uint32 priceTimestamp) = IDataStreamsVerifier(
           upkeep
       ).verifyReportWithTimestamp(unverifiedReport, game.feedId);
       require(
           block.timestamp - priceTimestamp <= 10 minutes,
           "Old chainlink report"
       );

(int192 startingPrice, uint32 startTime) = IDataStreamsVerifier( 
           ITreasury(treasury).upkeep()
       ).verifyReportWithTimestamp(unverifiedReport, feedNumber);
       if (isLong) {
           require(
               uint192(startingPrice) / 1e14 > stopLossPrice ||
                   uint192(startingPrice) / 1e14 < takeProfitPrice,
               "Wrong tp or sl price"
           );
       } else {
           require(
               uint192(startingPrice) / 1e14 < stopLossPrice ||
                   uint192(startingPrice) / 1e14 > takeProfitPrice,
               "Wrong tp or sl price"
           );
       }
MEDIUM severity: There may exist a discordance between price units and a precision loss.. **Severity**: Medium	

**Status**: Acknowledged

**Description**

The whole protocol performs price down-escalations by dividing prices by `1e14`. These price variables are compared with prices directly from function parameters, which may come in terms of 18 decimals. This may lead to wrong...

Vulnerable code:
uint256 diff1 = game.initiatorPrice > uint192(finalPrice) / 1e14 
           ? game.initiatorPrice - uint192(finalPrice) / 1e14
           : uint192(finalPrice) / 1e14 - game.initiatorPrice;

if (isLong) {
           require(
               uint192(startingPrice) / 1e14 > stopLossPrice ||
                   uint192(startingPrice) / 1e14 < takeProfitPrice,
               "Wrong tp or sl price"
           );
       } else {
           require(
               uint192(startingPrice) / 1e14 < stopLossPrice ||
                   uint192(startingPrice) / 1e14 > takeProfitPrice,
               "Wrong tp or sl price"
           );
       }
LOW severity: Users Can Make A Safe Bet In UpDown Game And Increase Their Chances Of Winning. **Severity** - Low

**Status** - Resolved

**Description**

The users in the up down game need to bet if the price would go up or down which is calculated with respect to the starting price which is set after stopPredictAt time . This means that after every user has predicted the price movement...

Vulnerable code:
require(
            game.stopPredictAt >= block.timestamp,
            "Game is closed for new players"
        );

require(block.timestamp >= game.stopPredictAt, "Too early");

require(
            game.stopPredictAt > block.timestamp,
            "Game is closed for new players"
LOW severity: `depositAmounts` marks an incorrect amount of funds after closing a game which may lead to wrong implications in future updates or external parts relying on this information.. **Severity**: Low

**Status**: Resolved

**Description**

If it is decided for a game to get closed by executing `closeGame()` within the `UpDown.sol` smart contract, the `depositAmounts` variable will show an incorrect amount of funds.

The `closeGame()` function is used to refund players with...

Vulnerable code:
function closeGame() public onlyRole(DEFAULT_ADMIN_ROLE) {
       for (uint i; i < UpPlayers.length; i++) {
           ITreasury(treasury).refund(
               depositAmounts[UpPlayers[i]],
               UpPlayers[i]
           );
       }
       delete UpPlayers;
       for (uint i; i < DownPlayers.length; i++) {
           ITreasury(treasury).refund(
               depositAmounts[DownPlayers[i]],
               DownPlayers[i]
           );
       }
       delete DownPlayers;
       emit UpDownCancelled(currentGameId);
       currentGameId = bytes32(0);
       packedData = 0;
   }
HIGH severity: _fractionOfShares Function in TeaVaultAmbient Contract May Cause the Vault to Round in the Incorrect Direction During Mathematical Operations. **Severity**: High

**Status**: Resolved

**Location**: TeaVaultAmbient.sol#_fractionOfShares

**Description**: 

The _fractionOfShares function is used in the TeaVaultAbient contract to aid the distribution and pricing of vault shares. This function takes a boolean value where true indicates the...

Vulnerable code:
function _fractionOfShares(
	uint256 _assetAmount,
	uint256 _shares,
	uint256 _totalShares,
	bool _isRoundingUp
  ) internal pure returns (
	uint256 amount
  ) {
	amount = _isRoundingUp ?
		_assetAmount.mulDivRoundingUp(_shares, _totalShares):
		_assetAmount.mulDiv(_shares, _totalShares);
   }
HIGH severity: Management Fee For The First Time Will Be Way Larger Than It Should Be. **Severity** - High

**Status** - Resolved

**Description**: 

In TeaVaultAmbient.sol, the lastCollectedManagementFee should be initialized as block.timestamp , if not when the management fee is accrued for the first time it will be accrued as →

As you can see since on declaration...

Vulnerable code:
function _collectManagementFee() internal returns (uint256 collectedShares) {
        uint256 timeDiff = block.timestamp - lastCollectManagementFee;
        if (timeDiff > 0) {
            unchecked {
                uint256 feeTimesTimediff = feeConfig.managementFee * timeDiff;//10000 * td
                uint256 denominator = (
                    FEE_MULTIPLIER * SECONDS_IN_A_YEAR > feeTimesTimediff?
                        FEE_MULTIPLIER * SECONDS_IN_A_YEAR - feeTimesTimediff:
                        1
                );
                collectedShares = totalSupply().mulDivRoundingUp(feeTimesTimediff, denominator);
MEDIUM severity: Expected price can get manipulated so that swap functionality is denied everytime. **Severity**: Medium	

**Status**: Acknowledged

**Description**: 

The function `executeSwap()` within the `TeamVaultAmbient.sol` contract, is used to swap tokens. One of the previous steps before `swap()`, is a call to `ambientImpact.calcImpact()` 

This `calcImpact` works like a swap...

Vulnerable code:
(int128 token0Flow, int128 token1Flow, ) = ambientImpact.calcImpact(                      _token0,
           _token1,
           poolIdx,
           _zeroForOne,
           _zeroForOne,
           _maxPaidAmount.toUint128(),
           0,
           _zeroForOne ? type(uint128).max : 0     // when buying, priceLimit is upper bound
       );

if (receivedAmount < baselineAmount) revert WorseRate(baselineAmount, receivedAmount);
MEDIUM severity: Incorrect Order of Operations in TeaVaultAmbient Will Result In token1 to Never Be Deposited. **Severity**: Medium

**Status**: Acknowledged

**Location**: TeaVaultAmbient.sol#deposit

**Description**: 

The deposit function of the TeaVaultAmbient allows a user to deposit into the protocol allowing their funds to be managed by a pool manager. This will let the pool manager deposit into...

Vulnerable code:
uint256 token0BalanceOfVault = _token0.getBalance(address(this)) - msg.value;
	 uint256 token1BalanceOfVault = _token1.getBalance(address(this)); 
	 _charge(_token0, _amount0Max);
	 _charge(_token1, _amount1Max);
MEDIUM severity: The Decimal Multiplier Would Not Work For Tokens With > 18 Decimals. **Severity** - Medium

**Status** - Acknowledged

**Description**: 

The DECIMAL_MULTIPLIER has been used for tokens such as USDC with 6 decimals (non 18 decimal tokens) , the multiplier would be applied here →

So if token was USDC , DECIMAL_MULTIPLIER woud be 1e12 to normalize the deposited...

Vulnerable code:
if (totalShares == 0) {
            // vault is empty, default to 1:1 share to token0 ratio (offseted by _decimalOffset)
            depositedAmount0 = _shares / DECIMALS_MULTIPLIER;
MEDIUM severity: Swap operation fails due to zero quantity in ambientSwap function. **Severity**: Medium

**Status**: Resolved

**Location**: TeaVaultAmbient.sol

**Description**: 

In the ambientSwap function of the TeaVaultAmbient.sol contract, the _maxPaidAmount is set to zero in cases involving non-native token transfers to ensure the value passed is zero. However, this also...

Vulnerable code:
// swap using Ambient pool
        bytes memory results = ambientSwapDex.userCmd{value:_maxPaidAmount}(
            paramsConfig.swapCallPath,
            abi.encode(
                token0,
                token1,
                poolIdx,
                _zeroForOne,
                _zeroForOne,
                _maxPaidAmount,   // @param qty
                0,
                _zeroForOne ? type(uint128).max : 0,     // when buying, priceLimit is upper bound
                _minReceivedAmount,
                0
            )
        );