CertiK reviewed XYRO (XYRO - audits) on 10 Jan 2025: 43 findings, 0 critical; 34 marked fixed, 9 open.
Report hosted on skynet.certik.com.
Trustblock does not review code.
Statuses are the security firm’s own, from its report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 3 | 0 | 3 |
| Medium | 1 | 0 | 10 |
| Low | 5 | 0 | 21 |
**Important Note:** Certain identification procedures were attempted to be applied to the project team in order to better understand the centralization situation and potential risks of the project. We strongly advise end users to conduct further research and exercise due diligence before engaging with the project given the centralization related risks. It is crucial for end users to independently verify and assess all available information to make informed decisions. ### Treasury #### ACCOUNTANT_ROLE In the contract `Treasury`, the role `ACCOUNTANT_ROLE` has authority over the functions listed below. - withdrawFees() Any compromise to the `ACCOUNTANT_ROLE` account may allow the hacker to take advantage of this authority and withdraw specified fees in a given token. #### DEFAULT_ADMIN_ROLE In the contract `Treasury`, the role `DEFAULT_ADMIN_ROLE` has authority over the functions listed below. - grantRole() - revokeRole() - setToken() - setSetupFee() - withdrawFees() - setUpkeep() - changeMinDepositAmount() Any compromise to the `DEFAULT_ADMIN_ROLE` account may allow the hacker to take advantage of this authority and set or unset token approval status, withdraw fees to a specified address, set the setup fee, change the minimum deposit amount, and set the upkeep address. **For example, if the `upkeep` address is not trusted, the game results could be manipulated.** #### DISTRIBUTOR_ROLE In the contract `Treasury`, the role `DISTRIBUTOR_ROLE` has authority over the functions listed below. - setGameToken() - depositAndLock() - depositAndLockWithPermit() - lock() - refund() - refundWithFees() - universalDistribute() - withdrawGameFee() - calculateRate() - withdrawInitiatorFee() - distributeBullseye() - bullseyeResetLockedAmount() - withdrawRakebackSetup() - setGameFinished() Any compromise to the `DISTRIBUTOR_ROLE` account may allow the hacker to take advantage of this authority and set the game token, lock the specified amount in the game account, set the game as finished, reset the locked amount for a game ID, deposit and lock tokens with a permit, withdraw rakeback setup, deposit and lock tokens with a permit, withdraw the initiator fee, refund tokens to a specified address, distribute funds to a specified address, refund amount with fees and conditions, distribute bullseye rewards to a specified address, calculate the rate for a given game ID, and withdraw the game fee and update the collected amount. ### Bullseye #### DEFAULT_ADMIN_ROLE In the contract `Bullseye`, the role `DEFAULT_ADMIN_ROLE` has authority over the functions listed below. - grantRole() - revokeRole() - setMaxPlayers() - setTreasury() - setExactRange() - setFee() - setRate() Any compromise to the `DEFAULT_ADMIN_ROLE` account may allow the hacker to take advantage of this authority and set the treasury address, set the rate for specific player configurations, set the fee to a new value, set the maximum number of players, and set the exact range. #### GAME_MASTER_ROLE In the contract `Bullseye`, the role `GAME_MASTER_ROLE` has authority over the functions listed below. - startGame() - finalizeGame() - closeGame() Any compromise to the `GAME_MASTER_ROLE` account may allow the hacker to take advantage of this authority and close the current game, finalize the game and distribute prizes, and start a new game session. ### OneVsOneExactPrice #### DEFAULT_ADMIN_ROLE In the contract `OneVsOneExactPrice`, the role `DEFAULT_ADMIN_ROLE` has authority over the functions listed below. - grantRole() - revokeRole() - changeGameDuration() - setTreasury() - setFee() - setRefundFee() - toggleActive() Any compromise to the `DEFAULT_ADMIN_ROLE` account may allow the hacker to take advantage of this authority and set the treasury address, change game duration, toggle the active status, set the fee and emit event, and set the refund fee. #### GAME_MASTER_ROLE In the contract `OneVsOneExactPrice`, the role `GAME_MASTER_ROLE` has authority over the functions listed below. - liquidateGame() - finalizeGame() Any compromise to the `GAME_MASTER_ROLE` account may allow the hacker to take advantage of this authority and liquidate a game if conditions are met, finalize game outcomes, and distribute rewards. ### Setup #### DEFAULT_ADMIN_ROLE In the contract `Setup`, the role `DEFAULT_ADMIN_ROLE` has authority over the functions listed below. - grantRole() - revokeRole() - changeGameDuration() - setTreasury() - setFee() - setInitiatorFee() - toggleActive() Any compromise to the `DEFAULT_ADMIN_ROLE` account may allow the hacker to take advantage of this authority and toggle the active state, change the game's duration limits, set the fee and emit event, set the initiator fee, and set the treasury address. #### GAME_MASTER_ROLE In the contract `Setup`, the role `GAME_MASTER_ROLE` has authority over the functions listed below. - closeGame() - finalizeGame() Any compromise to the `GAME_MASTER_ROLE` account may allow the hacker to take advantage of this
In the contract `Treasury`, the admin role has the authority to update the implementation contract behind the proxy contract. Any compromise to the admin account may allow a hacker to take advantage of this authority and change the implementation contract which is pointed by proxy and therefore execute potential malicious functionality in the implementation contract. **Important Note:** Certain identification procedures were attempted to be applied to the project team in order to better understand the centralization situation and potential risks of the project. We strongly advise end users to conduct further research and exercise due diligence before engaging with the project given the centralization related risks. It is crucial for end users to independently verify and assess all available information to make informed decisions.
In the contract `Treasury`, the role `DISTRIBUTOR_ROLE` has authority over the functions listed below. - setGameToken() - depositAndLock() - depositAndLockWithPermit() - lock() - refund() - refundWithFees() - universalDistribute() - withdrawGameFee() - calculateRate() - withdrawInitiatorFee() - distributeBullseye() - bullseyeResetLockedAmount() - withdrawRakebackSetup() - setGameFinished() Any compromise to the `DISTRIBUTOR_ROLE` account may allow the hacker to take advantage of this authority and directly withdraw tokens from the treasury. --- In the contract `Treasury`, the role `DEFAULT_ADMIN_ROLE` has authority over the functions listed below. - setToken() Any compromise to the `DEFAULT_ADMIN_ROLE` account may allow the hacker to take advantage of this authority and prevent players from withdrawing assets. **Important Note:** Certain identification procedures were attempted to be applied to the project team in order to better understand the centralization situation and potential risks of the project. We strongly advise end users to conduct further research and exercise due diligence before engaging with the project given the centralization related risks. It is crucial for end users to independently verify and assess all available information to make informed decisions.
The `calculateRakebackAmount` function calculates the rakeback based on the balance of `xyroToken` held by a user. This approach is vulnerable to manipulation, as users can temporarily transfer `xyroToken` to their account to increase their balance and receive a higher rakeback rate, thus reducing their asset loss. Below is the relevant code snippet: ```solidity function calculateRakebackAmount( address target, uint256 initialDeposit ) public view returns (uint256) { uint256 targetBalance = IERC20(xyroToken).balanceOf(target); if ( targetBalance < rakebackRate[0] * 10 ** IERC20Mint(xyroToken).decimals() ) { return 0; } uint256 rate; for (uint256 i = 10; i > 0; i--) { if ( targetBalance >= rakebackRate[i - 1] * 10 ** IERC20Mint(xyroToken).decimals() ) { rate = i; break; } rate = 0; } return (initialDeposit * rate * 100) / FEE_DENOMINATOR; } ```
In the `finalizeGame` function, there are two issues related to the handling of top player timestamps when determining the winners based on their guess differences. The relevant code snippet is: ```solidity for (uint256 i = 0; i < 3; i++) { if (currentDiff < closestDiff[i]) { for (uint256 k = 2; k > i; k--) { closestDiff[k] = closestDiff[k - 1]; topPlayers[k] = topPlayers[k - 1]; topIndexes[k] = topIndexes[k - 1]; } closestDiff[i] = currentDiff; topPlayers[i] = playerGuessData.player; topTimestamps[i] = playerGuessData.timestamp; topIndexes[i] = j; break; } else if ( currentDiff == closestDiff[i] && playerGuessData.timestamp < topTimestamps[i] ) { for (uint256 k = 2; k > i; k--) { closestDiff[k] = closestDiff[k - 1]; topPlayers[k] = topPlayers[k - 1]; topIndexes[k] = topIndexes[k - 1]; } topIndexes[i] = j; topPlayers[i] = playerGuessData.player; // Missing topTimestamps update break; } } ``` 1. **Issue with Moving Top Player Info:** When shifting the top player information, the `topTimestamps` array is not updated correspondingly. This may lead to incorrect evaluations of timestamp conditions. 2. **Missing Update of `topTimestamps`:** Under the condition `currentDiff == closestDiff[i] && playerGuessData.timestamp < topTimestamps[i]`, the `topTimestamps` array is not updated. This oversight can result in inaccurate timestamp comparisons in subsequent iterations.
There should always be events emitted in the sensitive functions that are controlled by centralization roles.
In the `Bullseye` contract, a player can join the game multiple times, and each entry is considered valid. However, during the game's finalization, if a player secures multiple positions within the `topPlayers` array, their rakeback is calculated multiple times. This may result in the winner's rewards inadvertently including portions of the losers' rakeback. This issue, while extreme, can occur under specific conditions. ```solidity for (uint i = 0; i < 3; i++) { if (currentRates[i] != 0) { winnersRakeback += ITreasury(treasury).lockedRakeback( currentGameId, topPlayers[i] ); } }
When the game master or a user calls the game's `createGame()` function and successfully creates a game, it does not return the corresponding `gameId`, which is very inconvenient.
The game contracts (`Bullseye`, `UpDown`, `Setup` and `OneVsOneExactPrice`) are serving as the underlying entity to interact with Chainlink data stream protocol. The scope of the audit treats third party entities as black boxes and assumes their functional correctness. However, in the real world, third parties can be compromised and this may lead to lost or stolen assets. In addition, upgrades of third parties can possibly create severe impacts, such as increasing fees of third parties, migrating to new LP pools, etc. ```solidity import {IDataStreamsVerifier} from "./interfaces/IDataStreamsVerifier.sol"; ... (int192 startingPrice, uint32 startTime) = IDataStreamsVerifier( ITreasury(treasury).upkeep() ).verifyReportWithTimestamp(unverifiedReport, feedNumber); ``` Besides, the `ITreasury(treasury).upkeep()` should be set as the trusted `DataStreamsVerifier` contract by the `DEFAULT_ADMIN_ROLE` of `Treasury` contract.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (15)
1,044 projects reviewed · 1,375 reports · latest May 2025
See CertiK’s auditsSomething 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