XYRO security record

Arbitrum, Ethereumxyro.ioXGitHub

As of 30 Oct 2026, XYRO has 4 audit reports by 2 security firms on Trustblock; the latest is by CertiK, dated 10 Jan 2025. Those reports list 1 critical acknowledged and not fixed.

Critical findings
1 critical acknowledged, not fixed
Reported in “Xyro” by Zokyo, Aug 2024. The team acknowledged it. See the findings
Open the latest report
Your label, live
Get the label for your site

Record last updated . It holds audits and findings, contracts and incidents up to Oct 2026. Trustblock does not review code.

Audits4

Newest first

AuditSecurity firmDateScopeCritical findingsReport
XYRO - auditsSecurity firm: CertiKDate: Scope: Repository at commit 0a08cfaNo open critical findingsOpen · skynet.certik.com
XyroSecurity firm: ZokyoDate: Scope: Repository at commit 8663619No open critical findingsOpen · IPFS
XyroSecurity firm: ZokyoDate: Scope: Repository at commit cd1ae21No open critical findingsOpen · IPFS
XyroSecurity firm: ZokyoDate: Scope: Repository at commit b7f77de1 critical acknowledged, not fixedOpen · IPFS

Findings86

Statuses are the security firm’s own, from each report.

SeverityOpenAcknowledgedFixed
Critical015
High307
Medium1219
Low5439
Critical`stopLoss` and` takeProfit` can be set in the same direction, leading to `stopLoss` voters be always the winnersXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
CriticalAnyone can drain the treasuryXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
CriticalFunds can be stolen after wrong approveXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
CriticalLock of fund due to the precision lossXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
CriticalUsers can lose funds due to `safeTransferFrom` after approvalXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
HighDue to Lack of Game State Check, `closeGame()` inXYRO - audits · CertiK · 10 Jan 2025Fixed

In the `closeGame()` function, as long as the game state is "Created" or the current time is three days after the game's `endTime`, an attacker can call `closeGame()` to refund the initiator's deposit repeatedly: `OneVsOneExactPrice.sol` ```solidity=405 function closeGame(bytes32 gameId) public { GameInfo memory game = decodeData(gameId); require(game.initiator == msg.sender, "Wrong sender"); require( game.gameStatus == Status.Created || ( block.timestamp > game.endTime ? block.timestamp - game.endTime >= 3 days : false ), "Wrong status!" ); ITreasury(treasury).refund( games[gameId].depositAmount, game.initiator, gameId ); //rewrites status games[gameId].packedData2 = (games[gameId].packedData2 & ~(uint256(0xFF) << 208)) | (uint256(uint8(Status.Cancelled)) << 208); emit ExactPriceCancelled(gameId); } ``` As a result, when the current time is three days after the `endTime`, there is no need to check the game state, and the refund can be made to the initiator. This allows `closeGame()` to be called repeatedly.For games in the "started" state, repeated calls by the initiator will also "refund" the opponent's deposit amount to initiator. In addition, when the game's lock record in treasury is large enough, the lock amount can be continuously withdrawn through this function. By combining this with the vulnerability in `createGameWithPermit`, which allows the repeated creation of a game with the same `gameId`, low-value tokens can be exchanged for high-value tokens.

Open the report
HighFailure to Reset `totalRakebackUp` and `totalRakeXYRO - audits · CertiK · 10 Jan 2025Fixed

In the `UpDown` contract, the `finalizeGame` function is responsible for finalizing the game and distributing rewards. However, when the game is canceled due to either `UpPlayers.length == 0` or `DownPlayers.length == 0`, the function resets several global storage variables but fails to reset `totalRakebackUp` and `totalRakebackDown`. This oversight can lead to incorrect calculations and potential asset loss in subsequent game rounds. The critical section of the code is: ```solidity if (UpPlayers.length == 0 || DownPlayers.length == 0) { // ... refund logic ... packedData = 0; totalDepositsUp = 0; totalDepositsDown = 0; currentGameId = bytes32(0); return; } // ... other logic ... delete DownPlayers; emit UpDownCancelled(currentGameId); packedData = 0; currentGameId = bytes32(0); return; // ... other logic ... delete DownPlayers; delete UpPlayers; ITreasury(treasury).setGameFinished(currentGameId); currentGameId = bytes32(0); packedData = 0; totalDepositsUp = 0; totalDepositsDown = 0; ``` The same issue is present at the end of the `finalizeGame` function where `totalRakebackUp` and `totalRakebackDown` are not reset, potentially affecting future games.

Open the report
HighThe `createGameWithPermit` Function in `OneVsOneEXYRO - audits · CertiK · 10 Jan 2025Fixed

In this function, there is no check like the one in `createGame` and `createGameWithDeposit` where `require(games[gameId].packedData == 0, "Game exists")` is used to verify if the current `gameId` already exists. As a result, when two `createGameWithPermit` transactions with the same `endTime` and both being public games (opponent is `address(0)`) are processed in the same block, the first created game will be overwritten by the second. Consequently, the user's bet for the first game will be locked in the contract and cannot be refunded or withdrawn through the game. In addition, since the locked amount for the game corresponding to the `gameId` has increased before the overwrite, this can be exploited in combination with the vulnerability that allows `closeGame()` to be called multiple times. This can lead to the exchange of low-value tokens for high-value tokens.

Open the report
HighFinal prices within a threshold of 10 minutes are accepted, leading to a different result than the real one and causing winners not earning fundsXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
HighFinalizing a game after 10 minutes will lock user’s earned fundsXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
Show 10 more
HighOne side players have more probability of winning than the otherXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
HighThe opponent has a higher probability of winning than the initiator of the gameXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
Medium`startingPrice` Not Set to Zero When the Game IsXYRO - audits · CertiK · 10 Jan 2025Fixed

In the case where `UpPlayers.length == 0 || DownPlayers.length == 0`, the game is invalid, and the game principal should be refunded to the players, followed by a return. However, before the return, `startingPrice` is not set to zero: `UpDown.sol` ```solidity=304 emit UpDownCancelled(currentGameId); packedData = 0; totalDepositsUp = 0; totalDepositsDown = 0; totalRakebackUp = 0; totalRakebackDown = 0; currentGameId = bytes32(0); return; ``` This will result in an existing `startingPrice` before the next game starts.

Open the report
MediumFailure to Reset `totalDepositsUp` and `totalDepoXYRO - audits · CertiK · 10 Jan 2025Fixed

When `uint192(finalPrice / 1e14) == _game.startingPrice`, after refunding the tokens to the user, the user's `depositAmounts` are not set to 0, and `totalDepositsUp` and `totalDepositsDown` are not reset to 0: `UpDown.sol` ```solidity=348 else if (uint192(finalPrice / 1e14) == _game.startingPrice) { for (uint i; i < UpPlayers.length; i++) { ITreasury(treasury).refund( depositAmounts[UpPlayers[i]], UpPlayers[i], currentGameId ); isParticipating[UpPlayers[i]] = false; } delete UpPlayers; for (uint i; i < DownPlayers.length; i++) { ITreasury(treasury).refund( depositAmounts[DownPlayers[i]], DownPlayers[i], currentGameId ); isParticipating[DownPlayers[i]] = false; } delete DownPlayers; emit UpDownCancelled(currentGameId); packedData = 0; currentGameId = bytes32(0); return; } ``` This will cause the variables from the current round to be included in the next round's game, leading to confusion. It may result in the `finalizeGame()` function for the next game being unable to execute.

Open the report
MediumIncorrect `Rakeback` Calculation for Multiple EntXYRO - audits · CertiK · 10 Jan 2025Fixed

In the `Treasury` contract, the `distributeBullseye` function is responsible for deducting the appropriate rakeback for each guess during reward distribution. However, if a player participates multiple times in a single round, the rakeback calculation may be incorrect. For instance, if userA participates twice with different guesses and userB participates once, and userA's second guess wins, both userA and userB are meant to retain one share of rakeback, with userA winning the reward. The issue arises when calculating `winnersRakeback`, as all of userA's rakeback entries are considered, including those from losing guesses. This results in insufficient rakeback for players to withdraw. ```solidity for (uint i = 0; i < 3; i++) { if (currentRates[i] != 0) { winnersRakeback += ITreasury(treasury).lockedRakeback( currentGameId, topPlayers[i] ); } } for (uint256 i = 0; i < 3; i++) { if (topPlayers[i] != address(0)) { if (currentRates[i] != 0) { ITreasury(treasury).distributeBullseye( currentRates[i], totalRakeback - winnersRakeback, topPlayers[i], currentGameId, topRakeback[i] ); } } } ```

Open the report
MediumIncorrect Calculation of `rakeback` in `Bullseys`XYRO - audits · CertiK · 10 Jan 2025Fixed

The `playWithPermit` function of `Bullseye` contract incorrectly calculates and double-counts the rakeback. The line `uint256 rakeback = totalRakeback += ITreasury(treasury).depositAndLockWithPermit(...)` both assigns and adds the value of rakeback to `totalRakeback`, leading to an incorrect rakeback value being stored in `playerGuessData` and emitted in the `BullseyeNewPlayer` event. ```solidity function playWithPermit( uint256 assetPrice, ITreasury.PermitData calldata permitData ) public { ... @> uint256 rakeback = totalRakeback += ITreasury(treasury) .depositAndLockWithPermit( depositAmount, msg.sender, currentGameId, playerGuessData.length, permitData.deadline, permitData.v, permitData.r, permitData.s ); ... @> totalRakeback += rakeback; emit BullseyeNewPlayer( msg.sender, assetPrice, depositAmount, currentGameId, playerGuessData.length - 1, rakeback ); } ```

Open the report
MediumMissing Minimum Deposit Requirement in `lock` FunXYRO - audits · CertiK · 10 Jan 2025Fixed

The `lock` function is responsible for locking tokens for a game and is invoked by other functions like `playWithDeposit` and `createGameWithDeposit`. However, unlike the deposit functions `depositAndLock`, `depositAndLockWithPermit` etc , `lock` does not enforce the requirement that the amount must be greater than or equal to `minDepositAmount[token]`. This omission allows players to lock fewer tokens than intended, potentially undermining game mechanics. Below is the relevant code snippet: ```solidity function lock( uint256 amount, address from, bytes32 gameId, bool isRakeback ) public onlyRole(DISTRIBUTOR_ROLE) returns (uint256 rakeback) { address token = gameToken[gameId]; require(approvedTokens[token], "Unapproved token"); require(deposits[token][from] >= amount, "Insufficent deposit amount"); // Missing minDepositAmount[token] check if (isRakeback) { rakeback = calculateRakebackAmount(from, amount); lockedRakeback[gameId][from] += rakeback; } deposits[token][from] -= amount; locked[gameId] += amount; } ```

Open the report
MediumNo Cap on FeesXYRO - audits · CertiK · 10 Jan 2025Fixed

The `fee` variables in the contract have no set limits, allowing fees to potentially reach 100%. If this occurs, users would receive no tokens from the contract, resulting in a complete loss of their investment.

Open the report
MediumOpponent's Assets Locked on Game Closure in `closXYRO - audits · CertiK · 10 Jan 2025Fixed

In the `OneVsOneExactPrice` contract, the `closeGame` function allows the game's initiator to close the game and refund tokens when the game is in the `Created` status or when the current time is three days after `game.endTime`. However, if the game is `Started`, the function only refunds the initiator's assets and does not account for the opponent's assets. Furthermore, there are no other functions available to refund the opponent's assets, potentially causing their assets to be locked indefinitely. The relevant code snippet is: ```solidity function closeGame(bytes32 gameId) public { GameInfo memory game = decodeData(gameId); require(game.initiator == msg.sender, "Wrong sender"); require( game.gameStatus == Status.Created || ( block.timestamp > game.endTime ? block.timestamp - game.endTime >= 3 days : false ), "Wrong status!" ); ITreasury(treasury).refund( games[gameId].depositAmount, game.initiator, gameId ); //rewrites status games[gameId].packedData2 = (games[gameId].packedData2 & ~(uint256(0xFF) << 208)) | (uint256(uint8(Status.Cancelled)) << 208); emit ExactPriceCancelled(gameId); } ```

Open the report
MediumPossible Failure for Last Winner to Claim RewardsXYRO - audits · CertiK · 10 Jan 2025Fixed

In the `finalizeGame()` function, the initiator fee is rounded only once during the calculation for the winning side: ```solidity=555 finalRate = ITreasury(treasury).calculateRate( games[gameId].totalDepositsTP - ((games[gameId].totalDepositsTP * initiatorFee) / FEE_DENOMINATOR), games[gameId].totalRakebackSL, gameId ); ``` However, in the `retrieveRewards()` function, each winner may have their initiator fee rounded when withdrawing, which can lead to multiple rounding in the total calculation: ```solidity=635 ITreasury(treasury).universalDistribute( msg.sender, depositAmounts[gameIds[i]][msg.sender] - ((depositAmounts[gameIds[i]][msg.sender] * initiatorFee) / FEE_DENOMINATOR), gameIds[i], games[gameIds[i]].finalRate ); ``` In this case, the total rewards withdrawn by each winner may exceed the amount stored in `locked[gameId]`, causing some users' funds (either rakeBack or prize money, depending on who withdraws the reward last) to be unable to be extracted from the treasury.

Open the report
Show 10 more
MediumPotential Incorrect Fee Calculation in `withdrawIXYRO - audits · CertiK · 10 Jan 2025Fixed

In the `Treasury` contract, the `withdrawInitiatorFee` function is designed to calculate and distribute the initiator's fee from the game deposits. However, the formula used to calculate `withdrawnFees` is incorrect. The current calculation is: ```solidity withdrawnFees = (wonTeamDeposits + lostTeamDeposits * initiatorFee) / FEE_DENOMINATOR; ``` This formula erroneously applies the `initiatorFee` only to `lostTeamDeposits`,and the winning team will be charged a 0.01% fee, leading to an incorrect fee calculation. According to [xyro.io](https://xyro.io/onboarding/one-vs-one),the fee should be calculated based on the combined total of `wonTeamDeposits` and `lostTeamDeposits`, multiplied by the rate `initiatorFee/FEE_DENOMINATOR`. This error can result in an incorrect initiator fee, causing potential asset loss to the game initiator.

Open the report
MediumUpDown Game Finalizing Failure by Missing `isParXYRO - audits · CertiK · 10 Jan 2025Fixed

Due to forgetting to set `isParticipating` of `msg.sender` to `true`, `msg.sender` can play the same game round multiple times, with their `deposits` being set to the amount of their last bet: ```solidity=126 function playWithDeposit(bool isLong, uint256 depositAmount) public { require(depositAmount >= minDepositAmount, "Wrong deposit amount"); require(!isParticipating[msg.sender], "Already participating"); require( DownPlayers.length + UpPlayers.length + 1 <= maxPlayers, "Max player amount reached" ); GameInfo memory game = decodeData(); require( game.stopPredictAt > block.timestamp && (game.totalDepositsDown + depositAmount <= type(uint32).max || game.totalDepositsUp + depositAmount <= type(uint32).max), "Game is closed for new players" ); if (isLong) { //rewrites totalDepositsUp packedData = (packedData & ~(uint256(0xFFFFFFFF) << 168)) | ((depositAmount + game.totalDepositsUp) << 168); UpPlayers.push(msg.sender); } else { //rewrites totalDepositsDown packedData = (packedData & ~(uint256(0xFFFFFFFF) << 136)) | ((depositAmount + game.totalDepositsDown) << 136); DownPlayers.push(msg.sender); } depositAmounts[msg.sender] = depositAmount; ITreasury(treasury).lock(depositAmount, msg.sender); emit UpDownNewPlayer(msg.sender, isLong, depositAmount, currentGameId); } ``` An attacker can repeatedly place small bets (e.g., 1 USDT) and then place a larger bet (e.g., 2 USDT). As a result, all of their bets will be updated to the last bet's amount (2 USDT) during the game finalization.At this point, the treasury will revert due to an underflow when it tries to distribute funds, because the funds locked in the contract for that round of the game are insufficient. This results in the `finalizeGame()` transaction failing.

Open the report
Medium‘DISTRIBUTOR` AND `DEFAULT_ADMIN` ROLES CAN DRAIN THE WHOLE TREASURYXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
Medium`collectedFee` can be easily inflatedXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
Medium`endTime` can be set higher than `stopPredictAt` leading to a wrong behavior of the protocolXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
Medium`startTime` is not checked, allowing games with past prices to be startedXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
MediumAttacker Can Steal Initiator FeesXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
MediumCalculated commission cut does not align with the expected percentagesXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
MediumIf Approved Token Is A Fee-On-Transfer Token Then The Refund/Distribution Would Be BrokenXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
MediumPlaying is restricted for an unlimited amount of usersXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
Show 10 more
MediumReward Distribution Or Refunds Can Be Griefed If One Of The Address Gets BlacklistedXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
Low`refundWithFee` Should Not Calculate RakebackXYRO - audits · CertiK · 10 Jan 2025Fixed

For refunds, rakeback should not be calculated and added to the user's deposits. This portion of the rakeback is redundant and could potentially cause an underflow in `locked[gameId]`. Since the current game, `OneVsOneExactPrice`, does not calculate rakeback, underflow will not occur for now. However, future games may encounter this issue.

Open the report
LowAll Unstarted Games in` Setup` Are in the "CreateXYRO - audits · CertiK · 10 Jan 2025Fixed

Since the state at the first position in the `enum` corresponds to the number 0, any unstarted game is in state 0, i.e., the "Created" state. This makes the game state checks in some functions of the contract ineffective.

Open the report
LowConfirmation on Execution Time of `Setup` Game FiXYRO - audits · CertiK · 10 Jan 2025Fixed

In the `Setup` contract, the `GAME_MASTER_ROLE` has the ability to finalize the game before the designated end time. Finalization is permissible when the game's status is set to `Created`, and the `finalPrice` falls within the range of `stopLossPrice` and `takeProfitPrice`. This behavior is inconsistent with other games such as `Bullseye`, `OneVsOneExactPrice`, and `UpDown`, where finalization is only allowed after the `game.endTime` has been reached.

Open the report
LowInappropriate `exactRange` Value for 18 Decimal PXYRO - audits · CertiK · 10 Jan 2025Fixed

In the `Bullseye` contract, the `exactRange` variable is used to determine if a player's guessed price is sufficiently close to the `finalPrice`, which in turn affects the distribution rate. Previously, the price was represented with 4 decimals, and the default `exactRange` was set to `50000`. However, in the current version, the price is represented with 18 decimals. Continuing to use `50000` as the default `exactRange` value may lead to incorrect evaluations due to the increased precision. This could potentially affect the fairness and accuracy of the game's outcome.

Open the report
LowInconsistent `withdrawStatus` Update on RakebackXYRO - audits · CertiK · 10 Jan 2025Fixed

The `retrieveRewards` function in the smart contract allows users to claim rewards based on game outcomes. However, when withdrawing rakeback assets, the function does not update the `withdrawStatus` for the user, which could lead to inconsistency in tracking withdrawal statuses. Below is the relevant code snippet: ```solidity if (withdrawStatus[gameIds[i]][msg.sender] == UserStatus.SL) { require( ITreasury(treasury).lockedRakeback( gameIds[i], msg.sender ) != 0, "You lost" ); ITreasury(treasury).withdrawRakebackSetup( gameIds[i], msg.sender ); // Missing update to withdrawStatus here } ``` Due to the weak validation of game status in `closeGame()`, if the official mistakenly calls `closeGame()` after `finalizeGame()`, it will allow the losing players to refund their principal in the game after receiving the rakeBack.

Open the report
LowInherited Contracts Not Initialized In InitializeXYRO - audits · CertiK · 10 Jan 2025Fixed

Contract `Treasury` extends `AccessControlUpgradeable`, but the extended contract is not initialized by the current contract. Generally, the initializer function of a contract should always call all the initializer functions of the contracts that it extends.

Open the report
LowLack of `gameId` Existence Check in `createSetup(XYRO - audits · CertiK · 10 Jan 2025Fixed

`createSetup()` lacks a check for the existence of `gameId`. When a user creates two games with the same `gameId` at the same time, the second one will overwrite the first.

Open the report
LowLack of Checks in `setStartingPrice()` in `UpDownXYRO - audits · CertiK · 10 Jan 2025Fixed

`UpDown.sol` ```solidity=234 function setStartingPrice( bytes memory unverifiedReport ) public onlyRole(GAME_MASTER_ROLE) { GameInfo memory game = decodeData(); require(block.timestamp >= game.stopPredictAt, "Too early"); require( UpPlayers.length != 0 || DownPlayers.length != 0, "Not enough players" ); address upkeep = ITreasury(treasury).upkeep(); (int192 startingPrice, uint32 priceTimestamp) = IDataStreamsVerifier( upkeep ).verifyReportWithTimestamp(unverifiedReport, game.feedNumber); require( block.timestamp - priceTimestamp <= 1 minutes, "Old chainlink report" ); packedData |= uint192(startingPrice / 1e14) << 104; emit UpDownStarted(startingPrice, currentGameId); } ``` Since `setStartingPrice` only requires `block.timestamp` to be greater than or equal to `game.stopPredictAt` and does not require `block.timestamp` to be less than `game.endTime`, this allows the game master to set the starting price after the game has ended. The function does not check if the `startingPrice` in `packedData` is 0, which allows the game master to repeatedly call the function to set the `startingPrice` multiple times after `game.stopPredictAt`. The function only checks `require(block.timestamp - priceTimestamp <= 1 minutes, "Old chainlink report");`, but does not restrict the time difference between `priceTimestamp` and `game.stopPredictAt`, which allows an expired price to be set as the starting price.

Open the report
LowLack of End Time Validation in `Setup` Contract'sXYRO - audits · CertiK · 10 Jan 2025Fixed

In the `Setup` contract, the process of finalizing a game involves decoding the `finalPrice` and `endTime` from the `unverifiedReport`. However, unlike other game contracts, the `Setup` contract does not validate whether the `endTime` is outdated. This oversight can lead to the contract utilizing an outdated report, potentially causing incorrect game finalization and financial discrepancies. Code snippet: ```solidity (int192 finalPrice, uint256 endTime) = IDataStreamsVerifier( ITreasury(treasury).upkeep() ).verifyReportWithTimestamp(unverifiedReport, data.feedNumber); ```

Open the report
Show 10 more
LowMissing Deposit Amount Validation in Game ContracXYRO - audits · CertiK · 10 Jan 2025Fixed

In the `Bullseye` and `UpDown` contracts, `minDepositAmount` and `depositAmount` is set when starting a game. However, there is no validation to ensure that `minDepositAmount` and `depositAmount` matches the `minDepositAmount[token]` specified in the `Treasury` contract. This lack of validation can prevent players from joining the game if the deposit amount is incorrect.

Open the report
LowMissing Zero Address ValidationXYRO - audits · CertiK · 10 Jan 2025Fixed

The cited address input is missing a check that it is not `address(0)`.

Open the report
LowPermit Function Doesn't Comply with DAI TokenXYRO - audits · CertiK · 10 Jan 2025Fixed

Several contracts implement a feature allowing users to grant token allowances through a permit signature. However, there is a compatibility issue with the `DAI` stablecoin's unique permit function signature. The `DAI` contract at address `0x6B175474E89094C44Da98b954EedeAC495271d0F` defines its permit function with the following parameters: `function permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s)`. This signature differs from the expected format, resulting in failed transactions because the contract's parameters do not align with those required by DAI's permit function. Consequently, any attempt to execute permit transactions with the incorrect parameters will inevitably fail.

Open the report
LowPotential Game ID Collision RiskXYRO - audits · CertiK · 10 Jan 2025Fixed

In the provided code snippets, game IDs are generated using the `keccak256` hash function with parameters such as `block.timestamp`, `endTime`, `msg.sender`, and other variables. The concern arises when multiple instances of the same game contract are deployed, and they share a common treasury contract. This can lead to potential game ID collisions. OneVsOneExactPrice Contract: ```solidity bytes32 gameId = keccak256( abi.encodePacked(endTime, block.timestamp, msg.sender, opponent) ); ``` Setup Contract: ```solidity bytes32 gameId = keccak256( abi.encodePacked( block.timestamp, endTime, takeProfitPrice, stopLossPrice, msg.sender ) ); ``` If the same parameters are used across different game contracts, the generated game IDs might be identical. When these IDs are used in a shared treasury contract, they could interfere with each other, potentially leading to asset loss and other unintended side effects.

Open the report
LowPotential Insufficient Time Gap Between `stopPredXYRO - audits · CertiK · 10 Jan 2025Fixed

The `startGame` function allows the creation of a game with specified parameters including `endTime` and `stopPredictAt`. Currently, it only checks if `endTime` is greater than `stopPredictAt`: `UpDown.sol` ```solidity=74 require(endTime > stopPredictAt, "Ending time must be higher"); ``` or `endTime` is greater than `block.timestamp`: `Bullseye.sol` ```solidity=91 require(endTime > block.timestamp, "Wrong ending time"); ``` This condition is insufficient as it does not enforce a significant time gap between `stopPredictAt` and `endTime`. If the gap is too small, players who join later have an advantage due to the predictability of price changes over short periods. This could lead to an unfair game environment.

Open the report
LowPrice Storage Size and Price Precision IssuesXYRO - audits · CertiK · 10 Jan 2025Fixed

In all games, the price precision used is 4 decimals. This is suitable for higher-priced tokens. However, for lower-priced tokens, this design doesn't effectively capture the price changes of the token. For example, for a token priced at \$1000, a price change of 0.00001%(\$0.0001) would be considered a price change in the game. However, for a token priced at \$0.01, a price change of 1%(\$0.0001) would need to occur before it is recognized as a price change. At the same time, using `uint32` to store 4-decimal prices allows for a maximum token price of around \$420,000. This will not be scalable in the future when token prices increase, and it also prevents the inclusion of some high-priced tokens, such as high-value LP tokens from Uniswap V2.

Open the report
LowRate Issues in BullseyeXYRO - audits · CertiK · 10 Jan 2025Fixed

According to the official documentation [xyro.io](https://xyro.io/onboarding/bulls-eye), when the number of participants is between 2 and 5, if the price is hit exactly, the first place receives 100%, otherwise, the first place gets 90% and the second place receives 10%. However, the code does not differentiate this situation and simply awards 100% of the rate bonus to the first place: `Bullseye.sol` ```solidity=14 uint256[3][5] public rates = [ [10000, 0, 0], [7500, 2500, 0], [9000, 1000, 0], [5000, 3500, 1500], [7500, 1500, 1000] ]; ``` `Bullseye.sol` ```solidity=306 if (packedGuessData.length <= 5) { ITreasury(treasury).withdrawGameFee( totalDeposited - depositAmount, fee, currentGameId ); currentRates = rates[0]; } else if (packedGuessData.length <= 10) { ITreasury(treasury).withdrawGameFee( totalDeposited - 2 * depositAmount, fee, currentGameId ); currentRates = closestDiff[0] <= exactRange ? rates[2] : rates[1]; } else { ITreasury(treasury).withdrawGameFee( totalDeposited - 3 * depositAmount, fee, currentGameId ); currentRates = closestDiff[0] <= exactRange ? rates[4] : rates[3]; } ``` There is an error in the official documentation regarding the distribution for exact price hits when there are 10+ players. It should align with the contract: 1st place - 75%, 2nd place - 15%, 3rd place - 10%. Additionally, the current `getRateIndex` function returns an index with a maximum value of 5, but the largest index in the `rates` array is 4: `Bullseye.sol` ```solidity=453 function getRateIndex( uint256 playersCount, bool isExact ) public pure returns (uint256 index) { if (playersCount <= 5) { index = isExact ? 1 : 0; } else if (playersCount <= 10) { index = isExact ? 3 : 2; } else { index = isExact ? 5 : 4; } } function setRate( uint256[3] memory rate, uint256 playersCount, bool isExact ) public onlyRole(DEFAULT_ADMIN_ROLE) { rates[getRateIndex(playersCount, isExact)] = rate; } } ```

Open the report
LowSolidity version 0.8.20 may not work on other chaXYRO - audits · CertiK · 10 Jan 2025Fixed

The compiler for Solidity 0.8.20 switches the default target EVM version to [Shanghai](https://blog.soliditylang.org/2023/05/10/solidity-0.8.20-release-announcement/#important-note), which includes the new `PUSH0` op code. This op code may not yet be implemented on all L2s, so deployment on these chains will fail. To work around this issue, use an earlier [EVM](https://docs.soliditylang.org/en/v0.8.20/using-the-compiler.html?ref=zaryabs.com#setting-the-evm-version-to-target) [version](https://book.getfoundry.sh/reference/config/solidity-compiler#evm_version)

Open the report
LowSolidity Version 0.8.23 Won't Work For All ChainsXYRO - audits · CertiK · 10 Jan 2025Fixed

Since Solidity Release 0.8.23, `MCOPY` opcode is introduced. However, this may not be compatible with all chains and L2s. As a result, the compatibility of the code could be affected.

Open the report
LowThe unvalidated `stopPredictAt` and `feedNumber`XYRO - audits · CertiK · 10 Jan 2025Fixed

In `startGame()`, there is no validation to check if `stopPredictAt` is greater than the current `block.timestamp`, and `feedNumber` is not validated to ensure it is a valid `feedNumber` capable of producing a price.

Open the report
Show 10 more
LowUnprotected Upgradeable ContractXYRO - audits · CertiK · 10 Jan 2025Fixed

The `Treasury` logic contract does not protect the initializer. An attacker can front-run the `initialize` call and assume ownership of the logic contract. Once in control, the attacker can perform privileged operations, misleading users into believing that they are interacting with the legitimate owner of the upgradeable contract.

Open the report
LowWeak Game State Check in `closeGame` of `Setup` GXYRO - audits · CertiK · 10 Jan 2025Fixed

In `setUp`, as long as `block.timestamp > data.endTime`, the game master can call the `closeGame()` function: `Setup.sol` ```solidity=355 function closeGame(bytes32 gameId) public onlyRole(GAME_MASTER_ROLE) { GameInfo memory data = decodeData(gameId); require(data.startTime != 0, "Game doesn't exist"); require( ((data.startTime + (data.endTime - data.startTime) / 3 < block.timestamp && (data.SLplayers == 0 || data.TPplayers == 0)) || block.timestamp > data.endTime), "Wrong status!" ); //rewrites status games[gameId].packedData2 = (games[gameId].packedData2 & ~(uint256(0xFF) << 72)) | (uint256(uint8(Status.Cancelled)) << 72); emit SetupCancelled(gameId, data.initiator); } ``` When the game master mistakenly calls this function, changing the game’s status from "finished" to "cancelled", it will allow the losing player to refund their principal from the game, while preventing the winning player from receiving the reward. Only the principal can be refunded to winning player.

Open the report
LowFloating pragmaXyro · Zokyo · 26 Sep 2024Fixed

The report gives no description here.

Open the report
LowUnnecessary function declarationXyro · Zokyo · 26 Sep 2024Fixed

The report gives no description here.

Open the report
LowUnnecessary variable declarationsXyro · Zokyo · 26 Sep 2024Fixed

The report gives no description here.

Open the report
LowUnused importXyro · Zokyo · 26 Sep 2024Fixed

The report gives no description here.

Open the report
Low`depositAmounts` marks an incorrect amount of funds after closing a game which may lead to wrong implications in future updates or external parts r...Xyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
Low`endTime` can be set to a lower value than `startTime`Xyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
LowAdd A Sanity Check For End TimeXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
LowCollectedFees Is Not Paid Out AnywhereXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
Show 10 more
LowGame initiator is allowed to set his own address as opponent, leading to creating a non-payable gameXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
LowLack of eventsXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
LowMissing zero address checkXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
LowOverflow on updating the deposited amountXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
LowReentrance To Drain Funds If Approved Token Is A Callback Token (ERC777)Xyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
LowThe `calculateUpDownRate` function does not distribute feesXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
LowThe `refuseGame` functionality is uselessXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
LowThe `withdrawFees` and `withdrawRakeback` functions are not following the decimal notation from the rest of functions, which may lead to an incorre...Xyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
LowUser can play knowing the starting priceXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report
LowUsers Can Make A Safe Bet In UpDown Game And Increase Their Chances Of WinningXyro · Zokyo · 30 Aug 2024Fixed

The report gives no description here.

Open the report

Contracts

No deployed address is recorded for these audits.

The reports name a repository or files. Open a report to see its scope.

The repositories and files the reports name

Show this on your site

Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.

Content

Facts

Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.

Fixed as
Language

Style

Layout
Theme
Accent
Corners

Format

Size
Date
Embed as
HTML
<head>
<script src="https://widget.trustblock.run/project-widget.min.js" defer></script>
</head>
<body>
<div
data-tb-project-widget
data-tb-project-slug="xyro"
></div>
</body>

Paste the code where the label should appear. It uses no API credit and updates by itself.

Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.

Tell us