Gooeys

CertiKFiles named in the report

CertiK reviewed Gooeys.io (Gooeys) on 15 Jul 2022: 49 findings, 0 critical; 23 marked fixed, 26 open.

No open critical findings
Open Gooeys.io’s security record

The original report was removed from its address (seen 20 Oct 2026).

Trustblock does not review code.

Findings49

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

SeverityOpenAcknowledgedFixed
Critical000
High501
Medium202
Low19020
HighCentralization Risks in Goo.solOpen

Functions with the `isGameController()` modifier can either be used by any external address which is given `gameController` status, but also allows the owner to call. The function `mint()` for the `Goo` contract is one such function. Should any of the `gameController` or `owner` addresses be compromised, a hacker can gain control and mint any desired amount of tokens to any chosen address. In the contract `Goo` the role `_owner` has authority over the functions shown in the diagram below. Any compromise to the `_owner` account may allow the hacker to take advantage of this authority and set new gameControllers, enable or disable walletSupplyLimitsEnabled or update the treasury address. ![](https://accelerator-tasks-prod.acc.corp.certik.com/9a369ca7d8f74941883a6d3dfe134802/diagrams/centralization_Goo-Goo-_owner.svg)

HighCentralization Risks in GooeyFruit.solOpen

In the contract `GooeyFruit` the role `_owner` has authority over the functions shown in the diagram below. Any compromise to the `_owner` account may allow the hacker to take advantage of this authority and for example set a new value for the private variable `_uri` (inherited from `ERC1155.sol`) or change the `ALT_TOKEN` through the `loadAltToken()` function. ![](https://accelerator-tasks-prod.acc.corp.certik.com/9a369ca7d8f74941883a6d3dfe134802/diagrams/centralization_GooeyFruit-GooeyFruit-_owner.svg)

HighCentralization Risks in Gooeys.solOpen

In the contract `Gooeys` the role `_owner` has authority over the functions shown in the diagram below. Any compromise to the `_owner` account may allow the hacker to take advantage of this authority and, for example, load malicious address for `D_TOKEN`, `G_TOKEN` and `G_FOOD`, set new values for fees, pause the game or change the `baseURI` address. ![](https://accelerator-tasks-prod.acc.corp.certik.com/9a369ca7d8f74941883a6d3dfe134802/diagrams/centralization_Gooeys-Gooeys-_owner.svg)

HighInitial Token DistributionOpen

In the file Goo.sol, all of the `GTO` tokens are sent to the `msg.sender` when deploying the contract.

HighOwner Can Use Contracts to Interact with ModifiedOpen

The owner can update any address to be exempt from the contract check performed by the modifier `contractGweiLimited()`. While unprivileged external accounts cannot use contracts to interact with the `Gooey` contract functions like `tumble()`, `consumeFruit()`, `startQuest()` and so on, the owner can use a contract to do so.

MediumImproper Burning of `G_TOKEN`Open

It is assumed that `G_TOKEN` is a reference to the contract `Goo` in Goo.sol. When the function `safeTransferFrom()` is called using the `msg.sender` as the sender and `DEAD` as the recipient, this function call bypasses the traditional `burn()` function mechanism in the `ERC20` base contract. As such, rather than sending the tokens to `address(0)`, the address `DEAD` is used, forming an inconsistency in disposal. More importantly, using `transferFrom()` rather than `burn()` means the `totalSupply()` of the `Goo` token is not updated. This can create logical inconsistencies for the token.

MediumPseudo RandomnessOpen

From context, it appears that the only callers of this function should be either the owner or the other contracts, `Goo` and `Gooeys`. However, if that's not the case, and someone else is labeled a "game controller," then this function is vulnerable to attack. The `random` number generation is based on the result of hashing using `keccak256`. This randomness result can be computed by another `Gamecontroller`. Therefore this function call has the potential to be frontrun.

Low`gameStartBlock` Not UpdatedOpen

If `gameStarted` bool is changed from false to true more than one time, then `gameStartBlock` will not be updated to the current `block.number` all consequent times, since `gameStartBlock` value will no longer be 0. For example, in the scenario above, in the `checkIn()` function, the value for `lastUpdateBlock` would be checked against the block number in which the game was originally started, instead of the most recent time in which `gameStarted` status is changed.

Low`tx.origin` CheckOpen

The condition that that `tx.origin == msg.sender` is used to determine if the caller is a contract address or EOA. This places multiple restrictions on the modifier `contractGweiLimited` such as: multisignature wallets cannot call functions with this modifier and a loss of composability.

Low`walletSupplyLimitCheck` modifierOpen

Inside `Goo.sol` the modifier `walletSupplyLimitCheck` is used to prevent an address from containing more than allowed supply. This modifier limits the balance a single address can hold. However, any user can create an infinite number of wallets.

Show 10 more
LowCall `checkIn()` for any `tokenId`Open

Anyone can call the function `checkIn()` for any `tokenId`, whether or not they own the token. This may leave a user with a lack of control over when given attributes are updated for their own tokens.

LowCommented Out CodeOpen

In the contract `Gooeys.sol`, there are numerous functions and declarations that are commented out.

LowComparison to A Boolean ConstantOpen

Boolean constants can be used directly and do not need to be compared to true or false. Inside `Goo.sol` at line 37 this comparison is made ```soldiity gameControllers[msg.sender] == false ``` --- Inside `Goo.sol` at line 69 this comparison is made ```soldiity supplyLimitExemptions[_addr] == true ``` --- Inside `GooeyFruit.sol` at line 41 this comparison is made ```soldiity gameControllers[msg.sender] == false ``` --- Inside `Gooeys.sol` at line 187 this comparison is made ```soldiity gameControllers[msg.sender] == false ``` --- Inside `Gooeys.sol` at line 683 this comparison is made ```soldiity quests[gooey1].inProgress == false ``` --- Inside `Gooeys.sol` at line 684 this comparison is made ```soldiity quests[gooey2].inProgress == false ```

LowDiscussion on token decimalsOpen

In `Goo.sol`, initial supply of GOO token is 2 billion with a precision of 9 decimals. This contract inherits from an edited version of ERC20 OpenZeppelin base contract where decimals is edited to 9. Note that major currencies have an accuracy of 18 decimal places. Therefore external usage of this token may cause calculation problems when exchanging GOO tokens with other currencies, or interacting with external contracts outside the scope of this audit.

LowHardcoded valuesOpen

In `Gooeys.sol` there are many functions containing hardcoded values. A few examples are `getNewGenTumbles()` and `pickGeneticStat()`. Since there are no comments on what these values are used for, it is unknown whether they are the correct values.

LowHidden Assumption About `gameControllers` AddressOpen

In the context of the contracts Gooey.sol, GooeyFruit.sol, and Goo.sol, each contract is to have the status of "game Controller" in order to make external calls between the contracts when necessary. However, this is not explicitly stated within the file. Moreover, other addresses may be given the status of "game Controller" and in that scenario, may not work as intended with this contract.

LowHidden Assumption About External ContractsOpen

While it is assumed that `G_TOKEN` refers to the `Goo` contract, and `G_CORE` refers to the `Gooeys` contract, this is not explicitly stated within the documentation of the code. In each of Gooeys.sol and GooeyFruit.sol, there are state variables in which it is not explicitly clear what contracts are being used as input. Particularly: - For `Gooeys.sol`: `G_TOKEN`,`D_TOKEN`, `G_FOOD` and `GOOEYS_OLD` - For `GooeyFruit.sol`: `G_TOKEN`, `ALT_TOKEN` and `G_CORE` These variables may refer to unaudited contracts, and may not be compatible with the contract code.

LowInconsistency Between Documentation and CodeOpen

The values set in `rarityThresholds` mapping do not conform to what is declared in the docs here https://dogira.gitbook.io/goobook/game-mechanics/cosmetics#selection-rolls-item-pools For example for rare traits the declared values are: - Background / Foreground = 1 - Ears = 29 - Body = 18 - Mask = 23 - Eyes = 25 - Accessory = 5 but the values inside the code are: - Background / Foreground = 8 - Ears = 46 - Body = 40 - Mask = 44 - Eyes = 39 - Accessory = 10

LowLack of Input ValidationOpen

In `Goo.sol` at line 27 is not checked if `_treasury` address passed as the argument of the constructor is the zero address. --- In `Goo.sol` at line 43 within `walletSupplyLimitCheck()` modifier are not checked if `from` and `to` addresses passed as the argument of the constructor are the zero address. Furthermore is not checked also if the `amount` is greater than zero. --- In `Goo.sol` at line 52 within `setGameController()` function is not checked if `_addr` address passed as the argument of the function is the zero address. --- In `Goo.sol` at line 58 within `setSupplyLimitExemption()` function is not checked if `_addr` address passed as the argument of the function is the zero address. --- In `Goo.sol` at line 63 within `setWalletSupplyLimits()` function is not checked if `_addr` address passed as the argument of the function is the zero address. --- In `Goo.sol` at line 82 within `updateTreasury()` function is not checked if `_treasury` address passed as the argument of the function is the zero address. --- In `Goo.sol` at line 87 within `mint()` function is not checked if `to` address passed as the argument of the function is the zero address. Furthermore is not checked also if the `amount` is greater than zero. --- In `GooeyFruit.sol` at line 47 within `setGameController()` function is not checked if `_addr` address passed as the argument of the function is the zero address. --- In `GooeyFruit.sol` at line 51 within `setGameCore()` function is not checked if `_addr` address passed as the argument of the function is the zero address. --- In `GooeyFruit.sol` at line 93 within `mintRoll()` function is not checked if `_addr` address passed as the argument of the function is the zero address. Furthermore is not checked also if the `_rollAmt` is greater than zero. --- In `GooeyFruit.sol` at line 116 within `loadGToken()` function is not checked if `_addr` address passed as the argument of the function is the zero address. --- In `GooeyFruit.sol` at line 128 within `loadAltToken()` function is not checked if `_addr` address passed as the argument of the function is the zero address. --- In `GooeyFruit.sol` at line 180 within `buyFood()` function is not checked if `_receipient` address passed as the argument of the function is the zero address. --- In `GooeyFruit.sol` at line 189 within `buyFoodAltToken()` function is not checked if `_receipient` address passed as the argument of the function is the zero address. --- In `Gooeys.sol` at line 193 within `loadExterns()` function are not checked if `_dToken`, `_tokenAddr` and `_foodAddr` addresses passed as the argument of the function are the zero address. --- In `Gooeys.sol` at line 240 is not checked if `_gooeysOld` address passed as the argument of the constructor is the zero address. --- In `Gooeys.sol` at line 455 within `tumbleNewGooey()` function is not checked if `tokenId` passed as the argument of the function already exists. --- In `Gooeys.sol` at line 884 within `setGameController()` function is not checked if `_addr` address passed as the argument of the function is the zero address.

LowMissing Emit EventsOpen

There should always be events emitted in the sensitive functions that are controlled by centralization roles.

Show 6 more
LowMissing Error MessagesOpen

When using a **require** or **revert** statement it is good practice to provide a string message containing details about the error that will be passed back to the caller. --- Related to this note, consider the function `convertOldGooeyCollection()`. This function uses a for-loop to cycle through several token ID values to convert from old NFTs to new. It is possible that during the loop, a token ID already exists between the _startId and _endId value. In this scenario, the function `convertOldGooey()` reverts and it is unclear which token ID value caused the issue. The owner will have to move through all values between the start and end values until the correct ID is excluded.

LowNo Validation Check for `gooey1` and `gooey2`Open

There is no check that the input values for `gooey1` and `gooey2` are not the same. The same tokenId cannot be used twice in `tumble()` function. Therefore the function `getTumbleCost()` should check that the input values are not the same.

LowNo Validation Check for Updating FunctionsOpen

For function `setTumbleFees()`, there is no check that the values for input `_tumbleBaseFee` and `tumbleScalingFee` do not go below or exceed certain values. This means that both values are unrestricted. Should the owner make a mistake on the input for these values, a user could potentially pay astronomical fees. The functions `updateGooTumbleWaitBlocks()`, `updateMaxNexus()` and `setDTokenDiscounts()` all have a similar issue in which the input values are not verified to be within a certain range. For `setDTokenDiscounts()`, if input `_dDiscountMultiplier` is a value higher than 100, then the user will receive no discount and in fact, have to pay more than the original `tumbleFee` calculated in the function `getTumbleCost()`. If `dDiscountMultiplier` is allowed to be value 0, then the user does not pay a tumble fee.

LowState variables not initialized at deployOpen

The contract `Gooeys.sol` is deployed without initializing the following state variables: `G_TOKEN`, `D_TOKEN` and `G_FOOD`. They will be initiated with address(0) as default. Between deploy and call to update the addresses through `loadExterns()`, there may be a span of time where logic could be corrupted.

LowUnhelpful Error MessagesOpen

In `Gooeys.sol`, one or more error messages related to require statements are not written to express the nature of the error when thrown.

LowUser-Defined GettersOpen

The linked functions are equivalent to the compiler-generated getter functions for the respective variables.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Repository and files the report names (3)

  • contracts/Gooeys.sol
  • contracts/Goo.sol
  • contracts/GooeyFruit.sol

Part of Gooeys.io’s security record

Gooeys.io
Audits1 audit by 1 firmLast audit Jul 2022, by CertiK.
Critical findingsNo open critical findingsIn 1 report.
Open Gooeys.io’s security record

Security firm

CertiK

1,044 projects reviewed · 1,375 reports · latest May 2025

See CertiK’s audits

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