Skip to content
$1,000,000 in security audit grants are live now, Apply here →

Security review · December 2024

Protocol Review

for Ginza Gaming

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

12 resolved · 11 acknowledged

Scope

4 files in scope · 354 nSLOC
FilenSLOCLines
onchain/src/IERC20Rebasing.sol49
onchain/src/IBlast.sol1217
onchain/src/GinzaStructs.sol1721
onchain/src/Ginza.sol321408

Findings 23

Main Review

16 findings · October 8 to 9, 2024
  1. H-01 High Automatic USDB Yields Are Trapped Logical Error Resolved
    Location
    Ginza.sol: 123
    Round
    Main Review

    Description

    In the initialize function 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.CLAIMABLE and 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.

  2. M-01 Medium Tables With Too Many Players May Get Stuck DoS Resolved
    Location
    Ginza.sol
    Round
    Main Review

    Description

    There is currently no cap on how many players can be added to a table, and in the updateTableCredits function these players must be iterated over to remove players and create the approved_players and player_credits arrays.

    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.

  3. M-02 Medium Zero Address Player Logical Error Resolved
    Location
    Ginza.sol: 164, 379
    Round
    Main Review

    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 _removePlayerFromTable will attempt to transfer ERCUSDB to the zero address, which reverts. Furthermore, if the user was added to the pending playersToRemove, the loop in updateTableCredits will revert causing DoS for the entire table. Ultimately, this player will never be able to get removed from the game.

    Recommendation

    Validate that the player is not address(0) in requestBuyInOnBehalf

  4. L-01 Low Arbitrary Players May Cancel Validation Resolved
    Location
    Ginza.sol: 172
    Round
    Main Review

    Description

    In the _cancelRequest function there is no validation to ensure that the player has an active request to cancel for the table.

    Thus a user can cause the PlayerCancelRequest event 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.

  5. L-02 Low Anyone Can emergencyExit Validation Resolved
    Location
    Ginza.sol: 365
    Round
    Main Review

    Description

    The emergencyExit function does not have an onlyApprovedPlayer modifier, therefore any arbitrary player can call the emergencyExit function on a stale table.

    Currently there is no effect beyond emitting the PlayerLeft event which may affect off-chain systems.

    Recommendation

    Consider adding an onlyApprovedPlayer modifier to the emergencyExit function.

  6. L-03 Low Lacking UUPS Init Call Best Practices Resolved
    Location
    Ginza.sol
    Round
    Main Review

    Description

    In the initialize function in the Ginza contract there is no invocation of the __UUPSUpgradeable_init function. There is no logic in the __UUPSUpgradeable_init function in the UUPSUpgradeable contract, however it is a best practice to call this function if this were to change in a future iteration.

    Recommendation

    Consider calling the __UUPSUpgradeable_init function in the initialize function.

  7. L-04 Low Tables Cannot Close Unexpected Behavior Acknowledged
    Location
    Ginza.sol
    Round
    Main Review

    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.

  8. L-05 Low Lacking CEI In _cancelRequest Best Practices Resolved
    Location
    Ginza.sol: 182
    Round
    Main Review

    Description

    In the _cancelRequest function the table.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.

  9. L-06 Low Lacking CEI In withdrawRake Best Practices Resolved
    Location
    Ginza.sol
    Round
    Main Review

    Description

    In the withdrawRake function the collectedRake entry for the user is reset to zero after the safeTransfer call 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.

  10. L-07 Low safeTransferFrom Should Occur First Best Practices Resolved
    Location
    Ginza.sol
    Round
    Main Review

    Description

    In the playerTopUp and _requestBuyIn functions the safeTransferFrom action 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 safeTransferFrom after all initial validations in a function, but before any of the accounting updates are performed in the playerTopUp and _requestBuyIn functions.

  11. L-08 Low Useful Event Data Best Practices Resolved
    Location
    Ginza.sol: 183
    Round
    Main Review

    Description

    In the PlayerCancelRequest a 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 PlayerCancelRequest event.

  12. L-09 Low Lacking updateTableCredits Validation Validation Acknowledged
    Location
    Ginza.sol: 229
    Round
    Main Review

    Description

    In the updateTableCredits function 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.

  13. L-10 Low getAllPlayerRequestsForTable DoS DoS Acknowledged
    Location
    Ginza.sol: 453
    Round
    Main Review

    Description

    The getAllPlayerRequestsForTable function 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 getAllPlayerRequestsForTable function 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.

  14. L-11 Low User Leaves Twice Events Acknowledged
    Location
    Ginza.sol
    Round
    Main Review

    Description

    A user can be kicked with function kickPlayer which will create a removal request and emit a PlayerLeave event. 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 a PlayerLeave event.

    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 playersToRemove if he was already directly removed with _removePlayerFromTable.

  15. L-12 Low Frontrunning Protocol Share Change Frontrunning Resolved
    Location
    Ginza.sol
    Round
    Main Review

    Description

    It is possible for a user to frontrun a call to setProtocolShare which would increase the share, and withdrawRake right before to pay the smaller fee.

    Recommendation

    Be aware of this behavior and document it appropriately.

  16. L-13 Low Player Can Top-up When Pending Removal Logical Error Resolved
    Location
    [Ginza.sol: 209](https://github.com/GinzaGaming/ginza/blob/402956f30b26beb847246250fc2f44e2c89ec702/onchain/src/Ginza.sol#L209)
    Round
    Main Review

    Description

    A user can call function playerTopUp to increase their credit even if they are in the playersToRemove list 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
  1. H-01 High Errant Credit Data Reported Logical Error Acknowledged
    Location
    Ginza.sol: 661
    Round
    Remediation Review

    Description

    The chainSync function returns table data in the order of approvedPlayers, credits, seats, pendingPlayers, and requestedAmounts. However the batchChainSync function 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 batchChainSync to agree with the ordering of the chainSync function.

  2. M-01 Medium Disable Initializers Removed Best Practices Acknowledged
    Location
    GinzaV4.sol
    Round
    Remediation Review

    Description

    In the GinzaV4 contract the constructor with the _disableInitializers invocation 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 _disableInitializers call in all Ginza contracts before production use.

  3. L-01 Low approveRequest Inefficient Seat Search Optimization Acknowledged
    Location
    Ginza.sol: 268
    Round
    Remediation Review

    Description

    In the approveRequest function the new player is added to the approvedPlayers list before the findNextAvailableSeat operation. The findNextAvailableSeat operation uses the approvedPlayers list to find each taken seat to deduce if a seat is available or not.

    Since the new player is added to the approvedPlayers list before the findNextAvailableSeat operation the findNextAvailableSeat function must add an additional unnecessary iteration for this new player for every seat that is checked.

    Recommendation

    Consider adding the new player to the approvedPlayers list after the findNextAvailableSeat operation to save on gas expenditure when approving a player.

  4. L-02 Low Seat Request Uncleared On Cancel Best Practices Acknowledged
    Location
    GinzaV4.sol: 204
    Round
    Remediation Review

    Description

    When creating a request to join a table the desired seat is recorded in the seatAssignments mapping, however in the _cancelRequest function the user is not removed from the seatAssignments mapping.

    Recommendation

    Remove all remnants of the player’s request from the seatAssignments mapping in the _cancelRequest function.

  5. L-03 Low Outdated Documentation Documentation Acknowledged
    Location
    GinzaV4.sol
    Round
    Remediation Review

    Description

    In the GinzaV4 contract the emergencyWithdraw threshold 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.

  6. L-04 Low Yield Is Automatic By Default Warning Acknowledged
    Location
    GinzaV4.sol: 121
    Round
    Remediation Review

    Description

    In the initialize function 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 intialize function.

  7. L-05 Low Unnecessary Stack Variable Optimization Acknowledged
    Location
    GinzaV4.sol: 693
    Round
    Remediation Review

    Description

    In the batchChainSync function the blockNumber_ 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 the chainSync function.

More from Ginza Gaming

  1. Forwarders

    53 findings6 critical · 6 high 53 findings: 6 critical, 6 high, 10 medium, 31 low

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.

Get a quote