Megamoon - Audit

CertiKFiles named in the report

CertiK reviewed MegaMoon (Megamoon - Audit) on 13 Oct 2022: 19 findings, 2 critical; 14 marked fixed, 5 open.

2 open critical findings
Open the reportOpen MegaMoon’s security record

Report hosted on skynet.certik.com.

Trustblock does not review code.

Findings19

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

SeverityOpenAcknowledgedFixed
Critical200
High201
Medium001
Low1012
HighPotential Reward Loss After `currentLotteryId` ReFixed

As the implementation of function `_updateUserProfit`, the users' reward and latest balance are updated in the lotteryId range between `user.lotteryId` and `currentLotteryId`. ```solidity=511 uint256 userId = user.lotteryId; if (user.lockedAmount > 0) { _userInjectionHistory[msg.sender][userId] = user.lockedAmount; _userRewardHistory[msg.sender][userId] = user.lockedAmount * lotteryInjectionInfos[userId].returnAmount / lotteryInjectionInfos[userId].amount; latestBalance += _userRewardHistory[msg.sender][userId]; userId += 1; } for (userId; userId < currentLotteryId; userId++) { uint256 injectAmount = latestBalance * lotteryInjectionInfos[userId].injectPercentage / 10000; _userInjectionHistory[msg.sender][userId] = injectAmount; uint256 rewardAmount = injectAmount * lotteryInjectionInfos[userId].returnAmount / lotteryInjectionInfos[userId].amount; _userRewardHistory[msg.sender][userId] = rewardAmount; latestBalance -= injectAmount; latestBalance += rewardAmount; } if (currentLotteryId == nextLotteryId || currentLotteryId == 0) { // lottery close user.lockedAmount = 0; user.stakingAmount = latestBalance; } else { user.lockedAmount = latestBalance * lotteryInjectionInfos[userId].injectPercentage / 10000; user.stakingAmount = latestBalance - user.lockedAmount; } user.lotteryId = userId; // currentLotteryId ``` But the `currentLotteryId` could be reset in the function `setLotteryAddress`. When the currentLotteryId is reset from `originLotteryId` to `newLotteryId`, the user whose lotteryId > `newLotteryId` will lose their rewards from `user.lotteryId` to the `originLotteryId`.

MediumShould Check If User Registered in `setActiveOrInFixed

The function `setActiveOrInactive` do not check if the user is registered before update `isActive[msg.sender]`, and the function is open to the public. Invalid user data could be set in the contracts by the attackers,

Low`_lotteryId` is Not Actually Used in `returnFundsFixed

Winthin function `returnFunds`, the input `_lotteryId` is used for emit event `ReturnFunds`. But the actual return fund logic uses the statement `currentLotteryId`.

Low`add()` Function Not RestrictedFixed

When the same LP token is added into a pool more than once in function `add()`, the total amount of reward in function `updatePool()` will be incorrectly calculated. The current implementation is relying on the operation correctness to avoid repeatedly adding the same LP token to the pool, as the function will only be called by the owner.

LowCheck Effect Interaction Pattern ViolatedFixed

The order of external call/transfer and storage manipulation should follow the check-effect-interaction pattern.

LowConfuse Variable Name `userId`Fixed

The Lottery Index used in `latestBalance` Calculation is named `userId` which is not named as how it used. ```solidity= uint256 userId = user.lotteryId; ```

LowDead CodeFixed

Modifier notContract is not used in contracts. ```solidity=112 modifier notContract() { require(!_isContract(msg.sender), "contract not allowed"); require(msg.sender == tx.origin, "proxy contract not allowed"); _; } ```

LowInconsistent Comment and CodeFixed

The inconsistent specification in comments: ```solidity=79 uint256 public constant MAX_PROCESSING_FEE = 10000; // 500 = 5% uint256 public constant MAX_WITHDRAW_FEE = 10000; // 500 = 5% uint256 public constant MAX_WITHDRAW_FEE_PERIOD = 744 hours; // 14 days (1 hour = 3600 blocks) ```

LowMissing Emit Events (Arithmetic Parameter)Fixed

One or more state changes do not emit events to pass the changes out of chain. ```solidity=207 lottery = IMegamoonLottery(lotteryAddress); currentLotteryId = lottery.viewCurrentLotteryId(); nextLotteryId = 1; ``` --- ```solidity=244 processingFee = _performanceFee; ``` --- ```solidity=253 withdrawFee = _withdrawFee; ``` --- ```solidity=265 withdrawFeePeriod = _withdrawFeePeriod; ``` --- ```solidity=275 minPriceTicketInMoon = _minPriceTicketInMoon; maxPriceTicketInMoon = _maxPriceTicketInMoon; ``` --- ```solidity=285 maxNumberTicketsPerBuyOrClaim = _maxNumberTicketsPerBuy; ``` --- ```solidity=295 referralBonus = _referralBonus; ``` --- ```solidity=679 BONUS_MULTIPLIER = multiplierNumber; ```

LowPotential Loss of Pool Rewards if `_withUpdate` iFixed

In the `add()` and `set()` functions, the flag '_withUpdate' determines if all the pools will be updated. This reliance might lead to a significant loss of the reward. For illustration, assume we have only one pool with `pool.allocPoint == 50` and `totalAllocPoint == 50` at the beginning. Now we want to add another pool with `pool.allocPoint == 50`. There will be two scenarios on calculating the pool reward, Case 1: `withUpdate` is set to `true`. - distribute the reward and update the pool. - add the given pool information Case 2: `withUpdate` is set to `false`. - add the given pool information (Note: While we focused on the `add()` function, the `updateAllocPoint()` and `set()` functions also update `totalAllocPoint`, which is used in calculation of pool rewards in the function `updatePool()` In Case 1, reward for the first pool is updated in the call to `updatePool()` where `neuronReward =multiplier.mul(pool.allocPoint).div(totalAllocPoint);` In Case 2, an update `totalAllocPoint = totalAllocPoint.add(_allocPoint)` is done first. Then `updatePool()` calculates the reward for the first pool: `neuronReward =multiplier.mul(pool.allocPoint).div(totalAllocPoint);`. Because the second pool is sharing rewards with the first one, the amount of reward for the first pool becomes half as much as that in the first case.

Show 4 more
LowPull-Over-Push Pattern In `transferOwnership()` FFixed

The change of `_owner` by function `transferOwnership()` overrides the previously set `_owner` with the new one without guaranteeing the new `_owner` is able to actuate transactions on-chain.

LowUnchecked ERC-20 `transfer()`/`transferFrom()` CaFixed

The return value of the transfer()/transferFrom() call is not checked. --- ```solidity=864 moon.transfer(_to, moonBal); ``` --- ```solidity=866 moon.transfer(_to, _amount); ```

LowUnnecessary Use of SafeMathFixed

The `SafeMath` library is used unnecessarily. With Solidity compiler versions 0.8.0 or newer, arithmetic operations will automatically revert in case of integer overflow or underflow. ```solidity=4 library SafeMath { ``` - An implementation of `SafeMath` library is found. --- ```solidity=617 using SafeMath for uint256; ``` - `SafeMath` library is used for `uint256` type in `MegaMoonPilot` contract. ```solidity=699 totalRate = totalRate.add(_moonPerBlock); ``` - `SafeMath.add` is called in `add` function of `MegaMoonPilot` contract. *Note: Only a sample of 2 `SafeMath` library usage in this contract (out of 41) are shown above.*

LowValue mismatch with the code commentsFixed

The value of the aforementioned constants doesn't match the comments. `100%` is not reasonable.

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 (8)

  • ReentrancyGuard.sol
  • Context.sol
  • Ownable.sol
  • ERC20.sol
  • Address.sol
  • IERC20.sol
  • IERC20Metadata.sol
  • SafeERC20.sol

Part of MegaMoon’s security record

MegaMoon
Audits1 audit by 1 firmLast audit Oct 2022, by CertiK.
Critical findings2 open critical findingsReported in “Megamoon - Audit” by CertiK, Oct 2022. Not marked fixed in that report.
Open MegaMoon’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