Guardian's review of Protocol Review for Ginza Gaming, published December 2024. The report records 23 findings across 2 review rounds, including 2 high and 3 medium.
- Published
- Review window
- October 8 to December 2, 2024
- Rounds
- Main Review, Remediation Review
- Language
- Solidity
- Chains
- Blast, Solana, Offchain
- Sector
- Gaming and prediction
- 0 Critical
- 2 High
- 3 Medium
- 18 Low
- 0 Informational
Scope
4 files in scope · 354 nSLOC
| File | nSLOC | Lines |
|---|---|---|
onchain/src/IERC20Rebasing.sol | 4 | 9 |
onchain/src/IBlast.sol | 12 | 17 |
onchain/src/GinzaStructs.sol | 17 | 21 |
onchain/src/Ginza.sol | 321 | 408 |
Findings 23
Main Review
16 findings · October 8 to 9, 2024-
H-01 High Automatic USDB Yields Are Trapped Logical Error Resolved
Description
In the
initializefunction the USDB yields are set to automatic, however there is no mechanism to claim the yield which will be reflected in an increasing USDB balance for the contract.Recommendation
Consider configuring
YieldMode.CLAIMABLEand implementing a function to claim the USDB yields that have accrued for the contract. A similar approach could be implemented for the WETH token, though it is not expected that the contract holds WETH. -
M-01 Medium Tables With Too Many Players May Get Stuck DoS Resolved
Description
There is currently no cap on how many players can be added to a table, and in the
updateTableCreditsfunction these players must be iterated over to remove players and create theapproved_playersandplayer_creditsarrays.These loops may cost an extreme amount of gas and even require more than the block gas limit to execute if there are many players in a single game. This could ultimately result in trapped funds for users, though they will be able to emergency withdraw.
Recommendation
Consider capping the total amount of players which can be added to a table.
-
M-02 Medium Zero Address Player Logical Error Resolved
Description
A user can request a buy-in for the zero address. If accepted, this user cannot be kicked from the game. This is because
_removePlayerFromTablewill attempt to transferERCUSDBto the zero address, which reverts. Furthermore, if the user was added to the pendingplayersToRemove, the loop inupdateTableCreditswill revert causing DoS for the entire table. Ultimately, this player will never be able to get removed from the game.Recommendation
Validate that the
playeris not address(0) inrequestBuyInOnBehalf -
L-01 Low Arbitrary Players May Cancel Validation Resolved
Description
In the
_cancelRequestfunction there is no validation to ensure that the player has an active request to cancel for the table.Thus a user can cause the
PlayerCancelRequestevent to be emitted even if they did not have a pending request. This may cause unexpected behavior for frontend systems or other consumers of the events from the Ginza contract.Recommendation
Consider reverting with a descriptive custom error if the
table.playerRequest.remove(player)operation returns false. Or performing a dedicated validation first thing in the function. -
L-02 Low Anyone Can emergencyExit Validation Resolved
Description
The
emergencyExitfunction does not have anonlyApprovedPlayermodifier, therefore any arbitrary player can call theemergencyExitfunction on a stale table.Currently there is no effect beyond emitting the
PlayerLeftevent which may affect off-chain systems.Recommendation
Consider adding an
onlyApprovedPlayermodifier to theemergencyExitfunction. -
L-03 Low Lacking UUPS Init Call Best Practices Resolved
Description
In the initialize function in the Ginza contract there is no invocation of the
__UUPSUpgradeable_initfunction. There is no logic in the__UUPSUpgradeable_initfunction in theUUPSUpgradeablecontract, however it is a best practice to call this function if this were to change in a future iteration.Recommendation
Consider calling the
__UUPSUpgradeable_initfunction in theinitializefunction. -
L-04 Low Tables Cannot Close Unexpected Behavior Acknowledged
Description
In the Ginza contract tables can only be opened and not closed.
Recommendation
Consider if this is the expected behavior. If not, consider implementing a function which that allows the table owner to close the table only after all players have left.
-
L-05 Low Lacking CEI In _cancelRequest Best Practices Resolved
Description
In the
_cancelRequestfunction thetable.playerRequest.remove(player)action is performed after transferring tokens to the user. However this accounting update ought to occur before giving the user their tokens to follow best practices.Recommendation
Consider performing the
table.playerRequest.remove(player)logic before transferring tokens to the user. -
L-06 Low Lacking CEI In withdrawRake Best Practices Resolved
Description
In the
withdrawRakefunction thecollectedRakeentry for the user is reset to zero after thesafeTransfercall sends their collected amount to the user. This is not immediately a risk as USDB is not a callback token, however it is a best practice to follow CEI for transfers to a user.Recommendation
Consider updating all accounting before making transfers to the user.
-
L-07 Low safeTransferFrom Should Occur First Best Practices Resolved
Description
In the
playerTopUpand_requestBuyInfunctions thesafeTransferFromaction occurs after all accounting updates have occurred.However it is a best practice to transfer funds in before performing state updates. This is because it is a valid state for the protocol to have received funds and not made accounting updates, but not for the protocol to have made accounting updates and not received funds.
Recommendation
Perform
safeTransferFromafter all initial validations in a function, but before any of the accounting updates are performed in theplayerTopUpand_requestBuyInfunctions. -
L-08 Low Useful Event Data Best Practices Resolved
Description
In the
PlayerCancelRequesta useful piece of information may be who is initiating the cancellation, as either the user or the table owner may do so.Recommendation
Consider if the canceler is a useful piece of information to add to the
PlayerCancelRequestevent. -
L-09 Low Lacking updateTableCredits Validation Validation Acknowledged
Description
In the
updateTableCreditsfunction there is no validation that a user is not in both of the winners and losers lists.Recommendation
Consider implementing validations such that no user may appear in both the winner and loser lists.
-
L-10 Low getAllPlayerRequestsForTable DoS DoS Acknowledged
Description
The
getAllPlayerRequestsForTablefunction iterates over all of the pending player requests to return the total player requests and amounts. There is no limit on how many player requests there are for a table, and there shouldn’t be, otherwise users could block preferred players from joining a game.However this means that the amount of player requests for a table may be extensive, even enough to overflow the block gas limit. For this reason the
getAllPlayerRequestsForTablefunction should be used for off-chain reading purposes only and no Smart Contract should attempt to integrate it into its own logic.Recommendation
Be aware of this risk and warn potential integrators about it.
-
L-11 Low User Leaves Twice Events Acknowledged
Description
A user can be kicked with function
kickPlayerwhich will create a removal request and emit aPlayerLeaveevent. If all other players are removed, the user can be kicked again and now will be directly removed from the table with_removePlayerFromTable. This will also emit aPlayerLeaveevent.This may cause issues with the UI relying on these events, since it appears as if the player left twice without joining in between.
Recommendation
Be aware of this behavior and consider removing a user from
playersToRemoveif he was already directly removed with_removePlayerFromTable. -
L-12 Low Frontrunning Protocol Share Change Frontrunning Resolved
Description
It is possible for a user to frontrun a call to
setProtocolSharewhich would increase the share, andwithdrawRakeright before to pay the smaller fee.Recommendation
Be aware of this behavior and document it appropriately.
-
L-13 Low Player Can Top-up When Pending Removal Logical Error Resolved
Description
A user can call function
playerTopUpto increase their credit even if they are in theplayersToRemovelist because they are still considered active until removal. This may be unexpected from a user’s perspective.Recommendation
Consider if this is intended behavior and document as necessary.
Remediation Review
7 findings · December 2, 2024-
H-01 High Errant Credit Data Reported Logical Error Acknowledged
Description
The
chainSyncfunction returns table data in the order of approvedPlayers, credits, seats, pendingPlayers, and requestedAmounts. However thebatchChainSyncfunction stores the returned values as approvedPlayers, seats, credits, pendingPlayers, and requestedAmounts where the seats and credits values are switched.This will mistaken the players seat number for their credit amount and cause significant loss for the players using the system relying on this reporting.
Recommendation
Correct the ordering of the values in the
batchChainSyncto agree with the ordering of thechainSyncfunction. -
M-01 Medium Disable Initializers Removed Best Practices Acknowledged
Description
In the
GinzaV4contract the constructor with the_disableInitializersinvocation has been removed. This is likely for testing purposes but the function call should be re-instated before deployment to avoid a malicious upgrade and self destruction of the implementation UUPS contract.Recommendation
Re-introduce the constructor with the
_disableInitializerscall in all Ginza contracts before production use. -
L-01 Low approveRequest Inefficient Seat Search Optimization Acknowledged
Description
In the
approveRequestfunction the new player is added to theapprovedPlayerslist before thefindNextAvailableSeatoperation. ThefindNextAvailableSeatoperation uses theapprovedPlayerslist to find each taken seat to deduce if a seat is available or not.Since the new player is added to the
approvedPlayerslist before thefindNextAvailableSeatoperation thefindNextAvailableSeatfunction must add an additional unnecessary iteration for this new player for every seat that is checked.Recommendation
Consider adding the new player to the
approvedPlayerslist after thefindNextAvailableSeatoperation to save on gas expenditure when approving a player. -
L-02 Low Seat Request Uncleared On Cancel Best Practices Acknowledged
Description
When creating a request to join a table the desired seat is recorded in the
seatAssignmentsmapping, however in the_cancelRequestfunction the user is not removed from theseatAssignmentsmapping.Recommendation
Remove all remnants of the player’s request from the
seatAssignmentsmapping in the_cancelRequestfunction. -
L-03 Low Outdated Documentation Documentation Acknowledged
Description
In the
GinzaV4contract theemergencyWithdrawthreshold has been reduced to 15 minutes, however the documentation in the contract suggests that the threshold is still 24 hours.Recommendation
Update the documentation to reflect the new threshold.
-
L-04 Low Yield Is Automatic By Default Warning Acknowledged
Description
In the
initializefunction the USDB yields are configured to automatic by default, however the functionality which exists to claim these yields is using a claimable yield mode.Recommendation
Instead of including the makeYieldClaimable function, consider making the yield claimable by default in the
intializefunction. -
L-05 Low Unnecessary Stack Variable Optimization Acknowledged
Description
In the
batchChainSyncfunction theblockNumber_stack variable is declared and never used.Recommendation
Remove the declaration of the
blockNumber_stack variable and instead leave a blank comma for the final return value from thechainSyncfunction.
No findings match.
More from Ginza Gaming
Put your code through the same review.
This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.
