MUMUBIT TOKEN security record

BNB Chain, Ethereum, Polygon (POS)mumubit.comXGitHub

As of 30 Oct 2026, MUMUBIT TOKEN has 4 audit reports by 1 security firm on Trustblock; the latest is by CertiK, dated 12 Mar 2025. Those reports list no open critical findings.

Critical findings
No open critical findings
Across the 4 reports, 5 critical findings were reported. Each is marked fixed by the firm that reported it. See the findings
Open the latest report
Your label, live
Get the label for your site

Record last updated . It holds audits and findings, contracts and incidents up to Oct 2026. Trustblock does not review code.

Audits4

Newest first

AuditSecurity firmDateScopeCritical findingsReport
MetacraftSecurity firm: CertiKDate: Scope: Repository at commit dac85faNo open critical findingsOpen · skynet.certik.com
mumubitSecurity firm: CertiKDate: Scope: Not recorded (see the report)No open critical findingsOpen · skynet.certik.com
Metacraft.Security firm: CertiKDate: Scope: Repository at commit 1393423No open critical findingsOpen · skynet.certik.com
Metacraft.Security firm: CertiKDate: Scope: Files named in the report5 critical findings, fixedOpen · skynet.certik.com

Findings145

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

SeverityOpenAcknowledgedFixed
Critical005
High1114
Medium2615
Low24077
CriticalA Way To Steal `bnb`Metacraft. · CertiK · 5 Sep 2022Fixed

The seller can finish the auction with different tokens in the function `finishAuction(string memory symbol_,uint256 _tokenId)`. Consider the following situation: 1. Bob creates an auction by account A. 2. Bob bids the auction with 100e18 `SHIBA` by account B. 3. At this time, Bob finishes the auction with the symbol `USDT`, and then 100e18 `USDT` will be paid to Bob. 4. Bob gets 100e18 `USDT` and pays 100e18 `SHIBA`. He pays less and gets more. ```solidity function finishAuction(string memory symbol_,uint256 _tokenId) public { ... ... IERC20(tokens[symbol_]).transferFrom(address(this),recipientAddr, receipientAmount); IERC20(tokens[symbol_]).transferFrom(address(this),auction.seller,sellerAmount); ... ... } ``` Round 2 **[Metacraft.pro]**: This function of finishAuction without a symbol of the chain, is set the bids with the default symbol of the chain(BNB in BSC, MATIC in Polygon and so on. **[Certik]**: The native token does not support the `transferFrom` function. Consider adding an native symbol and checking it in the function `finishAuction(uint256 _tokenId)`, for example: ```solidity function finishAuction(uint256 _tokenId) public payable { Auction memory auction = tokenIdToAuction[_tokenId]; require( msg.sender == auction.seller, "Should only be called by the seller" ); require(keccak256(abi.encodePacked("Native")) == keccak256(abi.encodePacked(auction.symbol)),"Symbol is not correct"); ... ... ```

Open the report
CriticalPotential Risk For Money Lost In `bidAuction`Metacraft. · CertiK · 5 Sep 2022Fixed

The users can bid the auction with different tokens in the function `bidAuction(string memory symbol_,uint256 price_,uint256 _tokenId)`. Consider the following situation: 1. Alice firstly bids the auction with 100e18 `USDT` and she is the highest bidder now. 2. Bob bids the auction with 200e18 `SHIBA` and 200e18 is greater than 100e18. Bob becomes the highest bidder and it will return 100e18 `SHIBA` to Alice. 3. Alice lost his 100e18 `USDT` and only gets 100e18 `SHIBA`. She almost lost all of the money. ```solidity function bidAuction(string memory symbol_,uint256 price_,uint256 _tokenId) public { .... .... if (price_ > highestBid) { ... ... if( highestBid > 0 ) { require(IERC20(tokens[symbol_]).approve(address(this), highestBid), "Approve has failed"); IERC20(tokens[symbol_]).transferFrom(address(this),highestBidder, highestBid); //payable(highestBidder).transfer(highestBid); } ```

Open the report
CriticalPotential Risk For Money Lost In `cancelAuction`Metacraft. · CertiK · 5 Sep 2022Fixed

The seller can cancel the auction with different tokens in the function `cancelAuction(string memory symbol_,uint256 _tokenId)`. Consider the following situation: 1. Bob creates an auction. 2. Alice firstly bids the auction with 100e18 `USDT`. 3. At this time, Bob cancels the auction with the symbol `SHIBA`, and then 100e18 `SHIBA` will be returned to Alice. 4. Alice pays 100e18 `USDT` and only gets 100e18 `SHIBA`. She pays more and gets less. ```solidity function cancelAuction(string memory symbol_,uint256 _tokenId) public { .... .... // if there are bids refund the last bid if( amount > 0 ) { require(IERC20(tokens[symbol_]).approve(address(this), amount), "Approve has failed"); IERC20(tokens[symbol_]).transferFrom(address(this),bidder,amount); // payable(bidder).transfer(amount); } .... .... } ```

Open the report
CriticalPotential Risk For Stealing Money In `finishAuctiMetacraft. · CertiK · 5 Sep 2022Fixed

The seller can finish the auction with different tokens in the function `finishAuction(string memory symbol_,uint256 _tokenId)`. Consider the following situation: 1. Bob creates an auction by account A. 2. Bob bids the auction with 100e18 `SHIBA` by account B. 3. At this time, Bob finishes the auction with the symbol `USDT`, and then 100e18 `USDT` will be paid to Bob. 4. Bob gets 100e18 `USDT` and pays 100e18 `SHIBA`. He pays less and gets more. ```solidity function finishAuction(string memory symbol_,uint256 _tokenId) public { ... ... IERC20(tokens[symbol_]).transferFrom(address(this),recipientAddr, receipientAmount); IERC20(tokens[symbol_]).transferFrom(address(this),auction.seller,sellerAmount); ... ... } ```

Open the report
CriticalWrong check for the balance of `msg.sender`Metacraft. · CertiK · 5 Sep 2022Fixed

```solidity require(payable(msg.sender).balance >=msg.value, "insufficient balance"); ``` The above check is meaningless since the native tokens have already been sent to this contract. For example, a user has 1 BNB and the price the user paid to bid the auction is also 1 BNB, then, in the above statement, the value of `payable(msg.sender)` is zero. As a result, the program is probably blocked.

Open the report
HighPotential Denial of Service Attack Caused by MaliMetacraft · CertiK · 12 Mar 2025Fixed

Within the contract, the `totalStakeAmount` variable signifies the current staked amount. However, in the `openStake()` function, there are no constraints on the parameters provided by the user, such as `_itemToken` and `_tokenAmount`. ```solidity function openStake( address _itemToken, uint256 _tokenAmount, uint256 _duration ) public whenNotPaused virtual { ... IERC20(_itemToken).transferFrom(msg.sender, address(this), _tokenAmount); ... totalStakeAmount+=_tokenAmount; stakeAmounts[_duration]+=_tokenAmount; ... } ``` If an attacker utilizes a malicious `itemToken` to execute this function, it could potentially manipulate `totalStakeAmount`, leading to a DoS attack. This situation also occurs in `stakeAmounts`.

Open the report
HighRewards may be claimed repeatedlyMetacraft · CertiK · 12 Mar 2025Fixed

In the `upgradeStake()` function, the contract calculates the user's reward using the remaining `tokenAmount` from `oldstake`, without updating the timestamp for reward reception afterward. This situation may allow the user to leverage the remaining `tokenAmount` from the current duration to repeatedly calculate and receive rewards. ```solidity function upgradeStake( address _itemToken, uint256 _tokenAmount, uint256 _oldDuration, uint256 _newDuration ) public whenNotPaused virtual { ... oldstake.tokenAmount -= _tokenAmount; ... payedProfits += stakeProfit; require(IERC20(_itemToken).approve(address(this), stakeProfit), "Approve has failed"); IERC20(_itemToken).transferFrom(address(this), oldstake.staker, stakeProfit); oldstake.reward = 0; // No update to oldstake.timestamp //totalStakeAmount -=_tokenAmount; if(newstake.staker == msg.sender){ ... }else { ... } ... } ``` Two potential attack scenarios arise: 1. Despite no restriction on upgrading to the same duration in `upgradeStake()`, an attacker could exploit this by upgrading to the same duration. In the `newstake`, the remaining tokenAmount from the current duration would be utilized to calculate rewards again. If the timestamp updates after calculating `oldstake`, upgrading to the same duration would not yield any rewards, as `newDuration_in_days` would be 0. 2. Following `upgradeStake()`, the attacker could utilize the `unStake()` function to withdraw the tokenAmount and its corresponding reward. As the duration's timestamp remains unchanged, the reward calculation within `unStake()` would still rely on the old timestamp. This situation may result in improper reward calculations and potentially exploit the system.

Open the report
HighPotential Underflow and Token Lock Risk in `withdMetacraft. · CertiK · 12 Apr 2024Fixed

In the `withdrawAllFunds()` function, there's a critical flaw where the total invested amount (`investedAmount[tokenAddress_]`) is reduced by the current balance of the token within the contract (`IERC20(tokenAddress_).balanceOf(address(this))`). This operation assumes that the contract's token balance only increases through legitimate investment actions via `investInWithVerify()`. However, an attacker could artificially increase the contract's token balance by directly transferring tokens to it. If such an action occurs, the subtraction operation could lead to an underflow in `investedAmount[tokenAddress_]`, potentially causing legitimate invested tokens to become permanently locked in the contract due to an inaccurate representation of available funds. ```solidity=702 function withdrawAllFunds(address tokenAddress_) external nonReentrant notContract(msg.sender) withdrawAddressOnly() { uint256 balance = IERC20(tokenAddress_).balanceOf(address(this)); require(investCoinAddress == tokenAddress_ || projectCoinAddress == tokenAddress_, "Coin Address Not Correct!"); require(block.timestamp - claimTimestamp >= 3 days, "Withdraw Fund Until 48 hours after Claim "); if(tokenAddress_ == investCoinAddress){ investedAmount[tokenAddress_] -= balance; refundedAmount[tokenAddress_] += balance; emit IDOStatusChange(tokenAddress_, balance, msg.sender, "Manager Withdraw invest Coins"); } else if(tokenAddress_ == projectCoinAddress){ projectCoinAmount[tokenAddress_] = IERC20(tokenAddress_).balanceOf(address(this)); claimedAmount[tokenAddress_] += balance; emit IDOStatusChange(tokenAddress_, balance, msg.sender, "Manager Withdraw invest Coins"); } else revert("withDraw TokenAddress ERROR"); IERC20(tokenAddress_).safeTransfer(msg.sender, balance); } ```

Open the report
HighSymbol Not CheckMetacraft. · CertiK · 5 Sep 2022Fixed

The given symbol is not checked in the function `executeTrade`.

Open the report
Medium`validOwners` Array Mishandles Duplicate ElementsMetacraft · CertiK · 12 Mar 2025Fixed

The array `validOwners` stores the current valid owner addresses. The `setOwner()` function either adds or removes valid owners based on the transaction operation. However, when adding a valid owner to the `validOwners` array, it does not check for duplicates, allowing re-addition of elements already present in the array. ```solidity function setOwner(address _owner, uint8 _status) public onlySelf { ... }else if(_status == 1) { validOwners.push(_owner); } } ``` Within the same function, removal of `validOwners` can be initiated based on the transaction. When scanning the `validOwners` array and encountering the target address, it will be removed. However, in the case of duplicate elements in the array, the `break` operation ensures that only the first matching address is removed, leaving subsequent duplicates unaffected in the array. This discrepancy between the length of `validOwners` and the count of valid signers will impact the execution of sign-related functions within the contract. ```solidity function setOwner(address _owner, uint8 _status) public onlySelf { _validOwners[_owner] = _status; if(_status == 0){ uint256 ownerLength = validOwners.length; for (uint256 i = 0; i < ownerLength; i++) { if (validOwners[i] == _owner) { validOwners[i] = validOwners[ownerLength - 1]; validOwners.pop(); break; } } } ... } ```

Open the report
Show 10 more
MediumDifferent `tokenId` Could have the Same URIMetacraft · CertiK · 12 Mar 2025Fixed

Usually, token URI points to data defining the NFT (attributes, images, etc.). It's usually set by the contract owner. A user that owns an NFT can just spoof any other NFT data by changing the token URI to any of the other NFTs. The `tokenURI()` function may return the same `tokenURI` for different tokenIds, leading to non-unique NFTs and making it difficult to distinguish between them. ```solidity function tokenURI(uint256 tokenId) public view virtual override(ERC721) returns (string memory) { ... // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked). if (bytes(_tokenURI).length > 0) { return string(abi.encodePacked(base, _tokenURI)); } ... } ```

Open the report
MediumFixed `MIN_SIGNATURES` in `executeSetTransaction`Metacraft · CertiK · 12 Mar 2025Fixed

The contract defines `MIN_SIGNATURES` as a constant value of 3. This setting adapts to changes for the number of valid owners (`ownerCount`) in the `signSetTransaction` function. However, there is no similar logic in the `executeSetTransaction` function. ```solidity // Function for authorized owners to sign pending transactions function signSetTransaction(uint transactionId) public validOwner notExecuted(transactionId) { ... uint signCount = ownerCount>=3?MIN_SIGNATURES:ownerCount; ... } ``` ```solidity function executeSetTransaction(uint transactionId) validOwner public { ... require(transaction.signatureCount >= MIN_SIGNATURES,"Signs Not Satisfied"); ... } ``` If the `removeOwner` function is used to reduce the number of owners to fewer than three, the system becomes inoperable for executing set transactions.

Open the report
MediumFixed `MIN_SIGNATURES`Can Lead to a Deadlock SituMetacraft · CertiK · 12 Mar 2025Fixed

The contract defines `MIN_SIGNATURES` as a constant value of 3. This setting does not adapt to changes in the number of valid owners (`_validOwners`). ```solidity uint constant MIN_SIGNATURES = 3; ``` ```solidity function setOwner(address _owner, uint8 _status) public onlySelf { _validOwners[_owner] = _status; } ``` ```solidity if (_settransactions[transactionId].signatureCount == MIN_SIGNATURES) { if (transaction.txType == TransactionType.SetSalesAddress){ _settransactions[transactionId].readyForExecutionTimestamp = block.timestamp + observationPeriod; } else{ _settransactions[transactionId].readyForExecutionTimestamp = block.timestamp; executeSetTransaction(transactionId); } } ``` If the `removeOwner` function is used to reduce the number of owners to fewer than three, the system becomes inoperable for actions requiring multi-signature consensus, since it's impossible to gather the necessary number of signatures. The contract does not have a mechanism to adjust the `MIN_SIGNATURES` based on the current number of valid owners. This inflexibility could limit the contract’s operational capability following changes in ownership structure.

Open the report
MediumFunction `transferFrom()` not recommendedMetacraft · CertiK · 12 Mar 2025Fixed

In an ERC20 contract, the `transferFrom()` or `safeTransferFrom()` function allows an authorized spender to transfer tokens on behalf of the owner. However, when the contract needs to transfer its own ERC20 tokens, it's recommended to use the `transfer()` or `safeTransfer()` function instead, because it is possible that the target token does not support transfer from the user itself. There are many similar operations in the contract. For example, in the `openStake()` function, this contract employs a combination of `approve` and `transferFrom` methods to transfer tokens to the target address. ```solidity function openStake( address _itemToken, uint256 _tokenAmount, uint256 _duration ) public whenNotPaused virtual { ... if(oldstake.staker == msg.sender){ ... require(IERC20(_itemToken).approve(address(this), stakeProfit), "Approve has failed"); IERC20(_itemToken).transferFrom(address(this), oldstake.staker, stakeProfit); } ... } ```

Open the report
MediumIncorrect Timestamp Difference AppliedMetacraft · CertiK · 12 Mar 2025Fixed

The contract employs `observationPeriod` and `maxPendingTime` to represent the time interval required for function execution, however, it directly uses the value 24, which translates to 24 seconds rather than 24 hours. ```solidity=120 uint256 public constant observationPeriod = 24 ; uint256 public constant maxPendingTime = 96 ; ``` ```solidity=335 // can not sign within once within 24 hours require(block.timestamp - transaction.timestamp >= 24,"Time Lockin for 24 Hours for each signers !"); ```

Open the report
MediumLack Of Access ControlMetacraft · CertiK · 12 Mar 2025Fixed

The specified function on the identified line is used to modify some critical or sensitive contract variables. However, it can be called by anyone as it has no access restriction. This enables malicious actors to call this and modify the `BaseURI` at will. ```solidity=398 function setBaseURI(string memory baseURI_) external { _baseURIextended = baseURI_; } ```

Open the report
MediumMissing Existing Check For `TxSetParam` In `signMetacraft · CertiK · 12 Mar 2025Fixed

The `signSetParam` function lacks a necessary check to ensure that a transaction has been properly initialized through the `setParameters` function before it can be signed. This omission allows valid owners to increase the `signatureCount` on an uninitialized `TxSetParam` transaction object, which could still hold the default values of its parameters (`locked_` and `halted_`). ```solidity function signSetParam(uint transactionId) validOwner public { TxSetParam storage transaction = _txsetparams[transactionId]; // Transaction must exist // Cannot sign a transaction more than once require(signatures[transactionId][msg.sender] != 1, "Can't Sign Again with the same Account!"); signatures[transactionId][msg.sender] = 1; transaction.signatureCount++; transaction.timestamp= block.timestamp; emit TransactionSigned(msg.sender, transactionId); if (transaction.signatureCount >= MIN_SIGNATURES) { locked = transaction.locked_; halted = transaction.halted_; emit TransactionCompleted(msg.sender, block.timestamp, transactionId); deleteSetParamTx(transactionId); } } ``` As a result, it is feasible that valid owners can sign an uninitialized transaction, meet the minimum signature requirements, and execute changes to the contract's state without ever setting explicit parameters through `setParameters`.

Open the report
MediumPossible Function Blockage Due to validOwners ArrMetacraft · CertiK · 12 Mar 2025Fixed

To accommodate fluctuations in the number of `validOwners`, the contract utilizes `validOwners` to store current valid owner addresses and adjusts the required number of signatures for function execution accordingly. However, the potential addition of duplicate elements to the `validOwners` array, coupled with the failure to remove duplicates when deleting specified elements, may result in invalid owner addresses being retained within the array. ```solidity function signSetTransaction(uint transactionId) public validOwner notExecuted(transactionId) { ... uint ownerCount = validOwners.length; uint signCount = ownerCount>=3?MIN_SIGNATURES:ownerCount; if (_settransactions[transactionId].signatureCount == signCount) { ... } ... } ```

Open the report
MediumWrong use of `newDuration_in_days` in upgradeStakMetacraft · CertiK · 12 Mar 2025Fixed

In the `upgradeStake()` function, users are allowed to upgrade their token staked from a lower to a higher duration level. The suggested upgradeable periods are 180, 356, and 730 days. Only when the duration is these three levels, users can get rewards by staking. ```solidity=353 uint256 newDuration_in_days = (block.timestamp.sub(newstake.timestamp)).div(SECONDS_IN_DAY); ///Seconds uint256 newstakeAPY = 0; uint256 newstakeProfit=0; if(newDuration_in_days == 180) newstakeAPY = newstake.tokenAmount.mul(half_APY).div(100).div(2); //half year if(newDuration_in_days == 365) newstakeAPY = newstake.tokenAmount.mul(full_APY).div(100); // full year if(newDuration_in_days == 730) newstakeAPY = newstake.tokenAmount.mul(two_APY).div(100).mul(2); // two years should multiply by 2 ``` When upgrading, if a user has previously staked tokens, the system will first calculate the existing staking rewards corresponding to the new duration. Therefore, it's important to assess the `newDuration` rather than the time interval `newDuration_in_days` since the user's initial staked.

Open the report
MediumWrong Visibility of `addSetTransaction`Metacraft · CertiK · 12 Mar 2025Fixed

The `addSetTransaction()` function implements a unified logic for the owner to add various set functions to pending transactions, such as `addOwner()`, `removeOwner()`, and `setParameters()` functions. However, its visibility is public, enabling the owner to bypass the set function and directly add pending transactions. This could lead to a lack of parameter checks or oversight of the length and status of current pending transactions.

Open the report
Show 10 more
MediumInconsistent `TonClaim.contract_balance`mumubit · CertiK · 21 Aug 2024Fixed

The `TonClaim` contract uses `contract_balance` to account for the `this_contract_projectCoinWallet` jetton balance. However, the value may become obsolete: 1. `WithdrawFunds` handler extracts the funds but doesn't update the `contract_balance`. 2. Assigning a different `this_contract_projectCoinWallet` via the `SetProjectCoinAddress` message makes the `contract_balance` irrelevant. 3. `TokenNotification` handler will only be executed if the jettons are sent to the contract wallet with non-zero `forward_ton_amount`. Otherwise, the `TonClaim` will not be notified and the funds will be blocked. 4. `AdminCorrectRecord` message can adjust the `contract_balance`, but only if `ido_claim_amount` is big enough.

Open the report
MediumLack Of Access ControlMetacraft. · CertiK · 12 Apr 2024Fixed

The function `removeOwner()` can be called by anyone as it has no access restriction. This enables anyone to call this and remove any multisig `_owners`.

Open the report
MediumTime Lock Bypass in Pending Proposal DeletionMetacraft. · CertiK · 12 Apr 2024Fixed

The `deleteMaxPendingTx()` function within the contract utilizes a time lock mechanism, `maxPendingTime`, intended to restrict the deletion of pending proposals until a certain period has passed. However, a valid owner can bypass this time lock by directly invoking `deleteTransaction()` to delete pending proposals.

Open the report
MediumLack Of Access ControlMetacraft. · CertiK · 5 Sep 2022Fixed

The function `setBaseURI` can be called by anyone as it has no access restriction. This enables anyone to call this and update the URI info of the contract.

Open the report
Low`nonReentrant` Modifier can be Placed and ExecuteMetacraft · CertiK · 12 Mar 2025Fixed

As a best practice, the `nonReentrant` modifier could be placed and executed before other modifiers in functions to prevent reentrancy attacks through other modifiers and make the code more efficient.

Open the report
Low`withDuration` can be bypassed through `upgradeStMetacraft · CertiK · 12 Mar 2025Fixed

In `upgradeStake()`, transitioning from a higher duration to a lower one is unrestricted. Consequently, if the user switches the current duration to 0, the duration check in unstake is effectively bypassed because it becomes 0. ```solidity function upgradeStake( address _itemToken, uint256 _tokenAmount, uint256 _oldDuration, uint256 _newDuration ) public whenNotPaused virtual { ... Stake storage oldstake = stakes[_itemToken][msg.sender][_oldDuration]; Stake storage newstake = stakes[_itemToken][msg.sender][_newDuration]; require(oldstake.staker == msg.sender, "staker address has no stake before in this duration"); ... if(newstake.staker == msg.sender){ ... }else { Stake memory stake = Stake({ itemToken: _itemToken, staker: msg.sender, tokenAmount: _tokenAmount, duration: _newDuration, reward : 0, timestamp:block.timestamp }); stakes[_itemToken][msg.sender][_newDuration] = stake; } ... } ``` In this scenario, user can promptly withdraw the tokenAmount that upgrades the duration without any waiting period.

Open the report
LowConcerns about `stakeTokenAddress`Metacraft · CertiK · 12 Mar 2025Fixed

We found that the newly declared `stakeTokenAddress` was initialized to a hard-coded [EOA address](https://etherscan.io/address/0x4fdB85CDa4eA74C5d55B45ACCc5dFaD58690A4F7) when the contract was deployed. We'd like to check with the team if this address is being used for testing purposes or if it will remain the same upon official deployment.

Open the report
LowConcerns about usage scenarios of `feeAddress` whMetacraft · CertiK · 12 Mar 2025Fixed

In the `setSalesAddress()` function, a new value is scheduled for update upon receipt of multiple validOwner signatures. However, during the signature waiting period, the old `feeAddress` remains active and is utilized in the `buyNode()` function to collect ERC20 Tokens from users. ```solidity function buyNode( address _to, string memory tokenURI_, address parentAddress //uint256 tokenPrice ) public returns (uint256){ ... ERC20Token.safeTransferFrom(msg.sender, feeAddress, mintPrice); ... } ```

Open the report
LowConflict Check to `tokenOwner[tokenId]` In `userFMetacraft · CertiK · 12 Mar 2025Fixed

The statements below could conflict if the `msg.sender` and the `receiver` are not the same address. This setup would make it logically impossible to pass both conditions unless `msg.sender` and `receiver` are identical. This might not be the intended logic, especially if the function is designed to allow a user (the current token owner) to specify another address (`receiver`) to receive the token. ```solidity function userFetchToken( uint256 tokenId, address receiver ) public nonReentrant notContract { require(tokenOwner[tokenId]== msg.sender,"Token Owner not Correct!"); require(tokenOwner[tokenId] == receiver, "Token receiver not Correct!"); ... } ```

Open the report
LowContract Lacks the Implementation of Function `onMetacraft · CertiK · 12 Mar 2025Fixed

The contract involves the transfer of ERC721 tokens, which means that the contract will be able to accept ERC721 tokens. However, the contract does not implement the `onERC721Received()` function. This can cause failures when others interact with it using `safeTransferFrom()`.

Open the report
Show 10 more
LowCould Use `safeTransferFrom` instead of `transferMetacraft · CertiK · 12 Mar 2025Fixed

Using `transferFrom` for NFT transfers might result in the NFT being sent to a contract address incapable of handling it, thereby locking the NFT in that contract address. However, with `safeTransferFrom`, it verifies if `onERC721Received` is implemented in the receiver account, ensuring its capability to handle the NFT properly. ```solidity function userFetchToken( uint256 tokenId )public { ... ERC721(this).transferFrom(address(this), msg.sender, tokenId); ... } ```

Open the report
LowDeprecated Usage of `Counters.sol`Metacraft · CertiK · 12 Mar 2025Fixed

The linked contract imports and uses OpenZeppelin's `Counters` contract. ```solidity=16 using Counters for Counters.Counter; ``` However, starting from version 0.5.0, Openzeppelin has deprecated the usage of the `Counters` contract: https://github.com/OpenZeppelin/openzeppelin-contracts/issues/4233

Open the report
LowDivide Before MultiplyMetacraft · CertiK · 12 Mar 2025Fixed

When the duration is set to 730, it implies that the stakeAPY should adhere to the two-year rule, that is, multiply by 2. However, potential precision loss during division calculations may lead to discrepancies from the expected outcome. For example: ```solidity=143 if(duration == 730) stakeAPY = stake.tokenAmount.mul(two_APY).div(100).mul(2); // two years should multiply by 2 ```

Open the report
LowDuplicate Signature CheckingMetacraft · CertiK · 12 Mar 2025Fixed

The `verified` function is somewhat redundant in the way it verifies the signature. The ECDSA library from OpenZeppelin provides a robust, tested method for recovering the signer address from a signature. Thus, it's unnecessary and less efficient to manually implement this functionality again using inline assembly. ```solidity require(recoveredSigner == expectedSigner, "Invalid signature"); ... require(ecrecover(ethSignedMessageHash, v, r, s) == expectedSigner, "Invalid signature malleability"); ```

Open the report
LowDuplicate with `transferFrom()` functionMetacraft · CertiK · 12 Mar 2025Fixed

The contract inherits from ERC721, which already includes a `transferFrom()` function that serves the same purpose as the `transNFT`. ```solidity function transNFT( address _from, address _to, uint256 tokenId ) public returns (uint256) { require(msg.sender == ownerOf(tokenId),"Only the owner of this Token could transfer It!"); transferFrom(_from,_to,tokenId); return tokenId; } ``` The `transferFrom` function already includes a check similar to `msg.sender == ownerOf(tokenId)`. ```solidity /** * @dev See {IERC721-transferFrom}. */ function transferFrom(address from, address to, uint256 tokenId) public virtual override { //solhint-disable-next-line max-line-length require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved"); _transfer(from, to, tokenId); } /** * @dev Returns whether `spender` is allowed to manage `tokenId`. * * Requirements: * * - `tokenId` must exist. */ function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { address owner = ERC721.ownerOf(tokenId); return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender); } ``` Moreover, the `buyNode` function first mints the new `newItemId` from `address(0)` to `msg.sender` and then transfers this token from `msg.sender` to `address(this)`. This process can be optimized to one `_mint(address(this), newItemId)` or `_safeMint(address(this), newItemId)`.

Open the report
LowInconsistency Usage of `hasStake`Metacraft · CertiK · 12 Mar 2025Fixed

In the `getStakeStatus()` function, users can provide a `tokenAddress` to query the `hasStake` status. However, at the end of the `openStake()` function, the `hasStake[msg.sender]` status, indicating whether the user has staked, is modified to `true`. ```solidity function getStakeStatus (address _tokenAddress) public virtual view returns (bool) { return hasStake[_tokenAddress]; } ``` ```solidity function openStake( address _itemToken, uint256 _tokenAmount, uint256 _duration ) public whenNotPaused virtual { ... hasStake[msg.sender] = true; ... } ```

Open the report
LowIneffective `notContract()` CheckMetacraft · CertiK · 12 Mar 2025Fixed

In the `MCTPStakeNew` contract, the implementation of the `notContract()` check can not cover all scenarios. The check can be bypassed if the call is from the constructor of a smart contract or when the contract is destroyed. Because, in that case, the code size will also be zero. ```solidity modifier notContract(address _address) { uint256 codeSize; assembly { codeSize := extcodesize(_address) } require(codeSize == 0, "Contracts are not allowed"); _; } ``` The "isContract" function in the OpenZeppelin "Address" library uses the same implementation, but comments mention that "it's unsafe to rely on the check and it can be bypassed". Reference: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol

Open the report
LowLack of Timelock on Adding/Deleting OwnersMetacraft · CertiK · 12 Mar 2025Fixed

To mitigate centralization concerns, a multi-signature approach is adopted for adding or removing a `validOwner`. However, there's no time delay implemented for the signing or execution of transactions by the owner. Consequently, an `validOwner` could potentially exercise all their rights immediately after gathering three signatures. ```solidity function signUserAdd(uint transactionId) validOwner public { ... signatures[transactionId][msg.sender] = 1; ... emit TransactionSigned(msg.sender, transactionId); if (transaction.signatureCount >= MIN_SIGNATURES) { _owners[(transaction.owner)]=1; emit TransactionCompleted(transaction.owner, block.timestamp, transactionId); deleteAddUserTx(transactionId); } } ```

Open the report
LowMissing Zero Address ValidationMetacraft · CertiK · 12 Mar 2025Fixed

Addresses are not validated before assignment or external calls, potentially allowing the use of zero addresses and leading to unexpected behavior or vulnerabilities. For example, transferring tokens to a zero address can result in a permanent loss of those tokens. - `_withdrawAddress` is not zero-checked before being used.

Open the report
LowNo Limit on Transaction Pending period or ExecutaMetacraft · CertiK · 12 Mar 2025Fixed

1. Transactions initiated via the `setWithdrawAddress()`, `signUserAdd`, and `signUserDel` function enter a pending state. They will execute once endorsed by three `validOwners`. However, without any signatures or with insufficient approvals, these transactions linger indefinitely in the pending queue until they meet execution prerequisites. Consequently, an outdated transaction might later receive approval and unexpectedly alter its designation. 2. Once a transaction garners the requisite three `validOwner` signatures and surpasses the `observationPeriod`, it becomes executable at any moment. Executing a transaction at an unforeseen time could potentially disrupt contract operations. Moreover, the lack of sequencing for these transactions may lead to newer transaction outcomes being inadvertently overridden by older ones, causing irregular contract functionality.

Open the report
Show 10 more
LowNot necessary using mapping to access constant vaMetacraft · CertiK · 12 Mar 2025Fixed

In the `buyNode()` function, each `tokenId`'s `tokenURI` is set to the constant variable `nodeTokenURI`. As a result, the `tokenURI` for different `tokenIds` remains the same. There is no necessity to use mapping to store identical `tokenURI` content. ```solidity string public constant nodeTokenURI = "ipfs://QmZ1n7S2UHFmBbiGFzFHYsTJoLwbU29rTDquxPccSNqajg"; function buyNode( uint256 timestamp, bytes32 nonce, bytes32 message, bytes memory signature, address parentAddress, uint256 maxPrice ) nonReentrant notContract external returns (uint256){ ... _tokenURIs[newItemId] = nodeTokenURI; ... } ```

Open the report
LowPotential Gas Optimizations By Swapping With TheMetacraft · CertiK · 12 Mar 2025Fixed

The deletion logic in the `deleteTransaction` function of the provided Solidity contract can be optimized. Currently, it uses a loop to shift all the elements after a certain index down by one to fill a gap created by deleting an element. This is not gas-efficient, especially if the array `_pendingTransactions` becomes large. ```solidity for(uint i = 0; i < _pendingTransactions.length; i++) { if (1 == replace) { _pendingTransactions[i-1] = _pendingTransactions[i]; } else if (transactionId == _pendingTransactions[i]) { replace = 1; } } delete _pendingTransactions[_pendingTransactions.length - 1]; _pendingTransactions.pop(); ``` Instead of shifting all elements after the deleted index, the last element of the array is moved into the spot of the element we want to remove. This reduces the cost of deleting an element from O(n) to O(1), since we only need to update one index position rather than potentially shifting a large number of elements.

Open the report
LowPotential Improper Timelock DesignMetacraft · CertiK · 12 Mar 2025Fixed

The `MCTPStake` contract includes a mechanism intended to secure updating the withdrawal address, by requiring multiple signatures from designated `owners`. The function `setWithdrawAddress` initiates transaction proposals, while `signTransaction` is used by owners to approve these proposals. ```solidity function signTransaction(uint transactionId) validOwner public { ... require(block.timestamp - transaction.timestamp >= 24 hours,"Time Lockin 48 Hours for at Least 2 signers after Tx created! !"); ... if (transaction.signatureCount >= MIN_SIGNATURES) { withdrawAddress = payable(transaction.withdrawAddress_); emit TransactionCompleted(transaction.withdrawAddress_, block.timestamp, transactionId); deleteTransaction(transactionId); } } ``` The `signTransaction` function requires each signature to occur at least 24 hours apart, totaling a 48-hour period which is designed to be the time window for owners to sign. As soon as the minimum signature threshold is met, the proposed transaction is executed instantly. The timelock period serves as a voting/approval period for owners. However, there is no isolation of the approval period (for signing by the owners) and the execution period (the window during which users can observe the approved transaction before it takes effect). Specifically, once a transaction has obtained the minimum required signatures, it is immediately executed without an additional delay. This design overlooks the need for a separate period during which stakeholders can detect and respond to impactful administrative actions. Without this observation period, users might not be able to make informed decisions, such as withdrawing their stakes in response to changes they disagree with or find alarming.

Open the report
LowPotential Inconsistent DecimalsMetacraft · CertiK · 12 Mar 2025Fixed

For ERC-20 tokens, the number of decimals can vary. A common standard is 18 decimals, just like ETH, but some tokens might use fewer. For instance, USDC, a popular stablecoin, uses 6 decimals. ```solidity function openStake( address _itemToken, uint256 _tokenAmount, uint256 _duration ) public whenNotPaused virtual { require(_tokenAmount >= minStakeAmount,"stake tokenAmount too small!"); ... totalStakeAmount+=_tokenAmount; stakeAmounts[_duration]+=_tokenAmount; ... } ``` When staking, the function imposes a minimum stake amount with a decimal precision of 18. This raises the threshold and complicates the utilization of the function for tokens with decimals other than 18, such as USDC. Additionally, the `totalStakeAmount` and `stakeAmounts[_duration]` accumulate the `_tokenAmount` from different `_itemToken`, making the value of these two state variables meaningless since each `_itemToken` may have different decimals.

Open the report
LowPotential Inconsistent Permission RestrictionsMetacraft · CertiK · 12 Mar 2025Fixed

1. In `executeTransaction()` function. This function can be triggered by any account without owner privilege, as previously mentioned by the team. However, within this function, the usage of `deleteTransaction()` is confined to `validOwner`. Consequently, this constraint means that in practice, only a validOwner is capable of actually carrying out the `executeTransaction()` function, leading to a situation that diverges from the anticipated operational scenario. ```solidity function executeTransaction(uint transactionId) public { ... deleteTransaction(transactionId); } modifier validOwner() { require(_owners[msg.sender] == 1, "not Authorized Mulsig User !"); _; } function deleteTransaction(uint transactionId) validOwner private { ... } ``` 2. In `deleteMaxPendingTx()` function. This function is accessible through owner-controlled (`owner`) operations such as `addOwner()`, `removeOwner()`, and `setWithdrawAddress()`. Within its scope, it carries out the deletion of pending transactions that have exceeded their timeout limit. However, the transaction removal function is exclusively callable by a `validOwner`, implying that only individuals who possess both owner and validOwner statuses can effectively implement the `deleteMaxPendingTx()` function's intended actions. ```solidity function addOwner(address _owner) public { require(msg.sender==owner,"Only owner can set Parameters"); deleteMaxPendingTx(); ... } function deleteMaxPendingTx() private { ... deleteTransaction(txId); ... } modifier validOwner() { require(_owners[msg.sender] == 1, "not Authorized Mulsig User !"); _; } function deleteTransaction(uint transactionId) validOwner private { ... } ```

Open the report
LowPotential Incorrect Last Pending Transaction DeleMetacraft · CertiK · 12 Mar 2025Fixed

The logic to remove a transaction attempts to shift all the subsequent elements in `_pendingTransactions` array leftward after finding the specified transactionId. However, it lacks proper validation to determine if the `transactionId` actually exists in the array. If it's not found, the code sets replace to false and will eventually delete the last item of `_pendingTransactions` regardless of whether it relates to the specified `transactionId`. ```solidity function deleteTransaction(uint transactionId) validOwner public { uint8 replace = 0; for(uint i = 0; i < _pendingTransactions.length; i++) { if (1 == replace) { _pendingTransactions[i-1] = _pendingTransactions[i]; } else if (transactionId == _pendingTransactions[i]) { replace = 1; } } delete _pendingTransactions[_pendingTransactions.length - 1]; _pendingTransactions.pop(); delete _transactions[transactionId]; } ```

Open the report
LowPotential Redundant Function `checkVerify` FunctiMetacraft · CertiK · 12 Mar 2025Fixed

The `checkVerify` and `splitSignature` functions seem to be functions for testing purpose. According to the official Solidity [documentation](https://docs.soliditylang.org/en/v0.8.18/solidity-by-example.html#id3), the function `splitSignature()` does not use all security checks. A real implementation should use a more rigorously tested library, such as openzepplin’s version of this code.

Open the report
LowPotential Reentrancy IssueMetacraft · CertiK · 12 Mar 2025Fixed

A reentrancy attack can occur when the contract creates a function that makes an external call to another untrusted contract before resolving any effects. If the attacker can control the untrusted contract, they can make a recursive call back to the original function, repeating interactions that would have otherwise not run after the external call resolved the effects. *If the itemToken is a malicious contract, the attacker's profit will only affect the itemToken, making it an inefficient operation for them. However, the reentrancy vulnerability may still cause other issues in the middle of transaction.* Reentrancy may occur in the following contracts because of the malicious itemToken contract: - `openStake()`: Allow users to participate in staking. - `upgradeStake()`: Upgrade stake from lower duration to higher Duration Level.

Open the report
LowPotential Risks Arising From Owner ChangesMetacraft · CertiK · 12 Mar 2025Fixed

The team uses a 3 of 3 multi-signature mechanism to weaken the centralization risk of the owner. However, changes in the `validOwner` might lead to operational uncertainties, especially in functions requiring multi-signature consensus. For instance, a transaction pending due to lack of enough approval from all valid owners might suddenly proceed if a new owner, who agrees, is introduced and signs off on it, thereby allowing the transaction to be executed and implemented.

Open the report
LowPotential Unintended Delay of Transaction ExecutiMetacraft · CertiK · 12 Mar 2025Fixed

The `signTransaction` function allows owners to sign a transaction for its execution. The condition for a transaction to become ready for execution (`transaction.readyForExecutionTimestamp`) is that it must be signed by at least `MIN_SIGNATURES` number of owners. Once this condition is met, the transaction is considered ready for execution after an observation period (`observationPeriod`, which is 24 hours in this case) has passed since the last signature was added. ```solidity function signTransaction(uint transactionId) validOwner public { ... if (transaction.signatureCount >= MIN_SIGNATURES) { transaction.readyForExecutionTimestamp = block.timestamp + observationPeriod; emit TransactionReadyForExecution(transaction.withdrawAddress_, transaction.readyForExecutionTimestamp, transactionId); } } ``` However, **if additional owners sign the transaction after it has already reached the minimum required signatures (`MIN_SIGNATURES`), the `transaction.readyForExecutionTimestamp` will be updated to 24 hours after the most recent signature**, pushing the execution window further into the future each time an additional signature is added. This behavior can potentially delay the execution of a transaction indefinitely, as long as additional owners continue to sign within each new 24-hour window.

Open the report
Show 10 more
LowRedundant `message` Parameter In `buyNode` FunctiMetacraft · CertiK · 12 Mar 2025Fixed

In the `buyNode` function, the caller must provide a `message` parameter which is supposed to match a challenge generated internally within the function using `getChallenge`. This design pattern is typically used to verify that the caller is indeed who they claim to be by validating a signature against a known hash. However, if the `message` is simply a hash of known parameters that are already inputs to the function (`timestamp, nonce, parentAddress, msg.sender`), then passing `message` as an input parameter is redundant. ```solidity function buyNode( uint256 timestamp, bytes32 nonce, bytes32 message, bytes memory signature, address parentAddress, uint256 maxPrice ) nonReentrant notContract external returns (uint256){ require(!halted, "Node Halted!"); bytes32 challenge = getChallenge(timestamp, nonce, parentAddress, msg.sender); require(message == challenge,"message not correct!"); ... } ```

Open the report
LowState Variable ShadowingMetacraft · CertiK · 12 Mar 2025Fixed

A state variable in a derived contract is shadowing a similarly named component in a parent contract. Employing variables with identical names, despite their distinct usage and access methods, can diminish the readability of the contract code. Variable `_owners` in `MetaCraftVNode` shadows the variable `_owners` in `ERC721`. In `MetaCraftVNode` contract: ```solidity=73 mapping(address => uint8) private _owners; ``` In `ERC721` contract: ```solidity mapping(uint256 => address) private _owners; ```

Open the report
LowState Variable Should Be Declared ConstantMetacraft · CertiK · 12 Mar 2025Fixed

State variables that never change should be declared as `constant` to save gas. ```solidity=77 uint256 public observationPeriod = 24 hours; ``` - `observationPeriod` could be declared `constant`. ```solidity=78 uint256 public maxPendingTime = 96 hours; ``` - `maxPendingTime` could be declared `constant`.

Open the report
LowThe Argument `_to` Can be RemovedMetacraft · CertiK · 12 Mar 2025Fixed

At the beginning of executing the `buyNode()` function, the provided parameter `_to` is constrained to `msg.sender`. As there's no discrepancy between `_to` and `msg.sender`, this parameter appears redundant and can be removed from the function.

Open the report
LowThe Calculation of Rewards Lacks Consideration FoMetacraft · CertiK · 12 Mar 2025Fixed

In the `unStake()` function, the parameter `withDuration` determines whether the user can unstake at any time. When `withDuration` is set to true, the unstake operation is restricted to occur outside the duration period. ```solidity=412 if(withDuration){ require( block.timestamp - stake.timestamp >= _duration * 1 days, //days for real project string(abi.encodePacked("UnStake should be More than ",_duration," Days")) ); } uint256 payprofit = 0; if(_duration == 180) payprofit = _tokenAmount.mul(half_APY).div(100).div(2); //half of year should divided by 2 if(_duration == 365) payprofit = _tokenAmount.mul(full_APY).div(100); // full year if(_duration == 730) payprofit = _tokenAmount.mul(two_APY).div(100).mul(2); // two years should multiply by 2 payedProfits += payprofit; uint256 payAmount = _tokenAmount.add(payprofit); require(IERC20(_itemToken).approve(address(this), payAmount), "Approve has failed"); IERC20(_itemToken).transferFrom(address(this), stake.staker, payAmount); ``` However, if the value is set to false, the unstake operation disregards the time difference when calculating the reward. Consequently, even if the user's stake duration is incomplete, they will still receive the same amount of rewards as if the stake had been completed for the full duration.

Open the report
LowTransaction May be Deleted Arbitrarily by `validOMetacraft · CertiK · 12 Mar 2025Fixed

The `deleteTransaction()` function is designed to remove any `setWithdrawAddress` transactions issued by the contract, with the operation being restricted to addresses deemed as `validOwner`. However, due to the absence of further constraints, any compromise to the `validOwner` account may allow the hacker to take advantage of this authority, this could disrupt the regular operation of both the `signTransaction()` function and the `withdrawAddress` feature. **Scenario** For a transaction to proceed as planned, it necessitates the endorsement of at least two `validOwner` signatures. However, suppose `signer3` has signed a transaction using the `signTransaction()` function. But then the `signer3` is in an unsafe state. A situation could arise where after `signer1` has signed this transaction, `signer3` might immediately delete it using the `deleteTransaction()` function. This action renders `signer1's` signature ineffective, leading to the transaction's failure to execute.

Open the report
LowTransfer Optimization In `buyNode`Metacraft · CertiK · 12 Mar 2025Fixed

The `buyNode` function first mints the token to the `msg.sender` and then immediately transfers it to `address(this)`. This involves two separate state changes on the blockchain which consume more gas than necessary. ```solidity _mint(msg.sender, newItemId); ... transferFrom(msg.sender, address(this), newItemId); ``` Instead, the function can mint the token directly to `address(this)` to save on gas costs associated with the token transfer transaction. This change would simplify the operation and reduce gas usage by eliminating the need for the `transferFrom` call.

Open the report
LowUnchecked ERC-20 `transfer()`/`transferFrom()` CaMetacraft · CertiK · 12 Mar 2025Fixed

The return values of the `transfer()` and `transferFrom()` calls in the smart contract are not checked. Some ERC-20 tokens' transfer functions return no values, while others return a bool value, they should be handled with care. If a function returns `false` instead of reverting upon failure, an unchecked failed transfer could be mistakenly considered successful in the contract.

Open the report
LowUnclear Purpose of `reStake()`Metacraft · CertiK · 12 Mar 2025Fixed

Upon reviewing the `reStake()` function, it was observed that the contract calculates and transfers the user's rewards and then re-stakes the previously staked `tokenAmount` to the contract again, with the only alteration being the timestamp.

Open the report
LowUnclear Usage of `tokenAmount` When Calculating `Metacraft · CertiK · 12 Mar 2025Fixed

In the `upgradeStake()` function, users have the option to upgrade the token amount from the current duration to another duration. Currently, in the `upgradeStake()` function, the tokenAmount slated for upgrade is deducted from `oldstake` initially, and the remaining token reward is then calculated and disbursed to the user. This process results in the reward for the upgraded tokenAmount never being calculated and claimed. ```solidity=328 oldstake.tokenAmount -= _tokenAmount; ... uint256 stakeAPY = 0; uint256 stakeProfit=0; if(_oldDuration == 180) stakeAPY = oldstake.tokenAmount.mul(half_APY).div(100).div(2); //half year if(_oldDuration == 365) stakeAPY = oldstake.tokenAmount.mul(full_APY).div(100); // full year if(_oldDuration == 730) stakeAPY = oldstake.tokenAmount.mul(two_APY).div(100).mul(2); // two years should multiply by 2 ```

Open the report
Show 10 more
LowUnnecessary `approve()`Metacraft · CertiK · 12 Mar 2025Fixed

When a user intends to stake or deposit, the ERC20 token is transferred from the user's account to the receiving contract. If the `transferFrom()` function is utilized, the user must pre-authorize the spender. However, the MCTPStake contract calls an unnecessary `token.approve()` for itself, it doesn't impact subsequent token transfers from user to this contract. For instance, within the `openStake()` function, the contract will grant authorization to itself before transferring tokens. ```solidity function openStake( address _itemToken, uint256 _tokenAmount, uint256 _duration ) public whenNotPaused virtual { ... require(IERC20(_itemToken).approve(address(this), _tokenAmount), "Approval failed"); IERC20(_itemToken).transferFrom(msg.sender, address(this), _tokenAmount); ... } ```

Open the report
LowUnnecessary `require` Condition LogicMetacraft · CertiK · 12 Mar 2025Fixed

In the `openStake()` function, a check ensures that the sum of `oldstake.tokenAmount` and `_tokenAmount` does not exceed `type(uint256).max`. ```solidity function openStake( address _itemToken, uint256 _tokenAmount, uint256 _duration ) public nonReentrant //anti re-entrancy whenNotPaused notContract(msg.sender) //anti contract address virtual { ... // stake more coins for a staking stage if(oldstake.staker == msg.sender){ ... require(oldstake.tokenAmount + _tokenAmount <= type(uint256).max, "uint256 type overFLow!"); ... oldstake.tokenAmount += _tokenAmount; } } ``` In Solidity, starting from version 0.8.0, arithmetic operations automatically check for overflow and underflow. This means that if an operation would result in a value outside the allowable range of the data type (for example, adding 1 to the maximum value of a `uint256`), the transaction will revert due to an overflow error. Given this, the explicit requirement `require(oldstake.tokenAmount + _tokenAmount <= type(uint256).max, "uint256 type overflow!");` in the `openStake` function is redundant. The Solidity compiler (version 0.8.0 and above) will automatically revert the transaction if `oldstake.tokenAmount + _tokenAmount` exceeds the maximum value.

Open the report
LowUnnecessary Storage Read Access in For LoopMetacraft · CertiK · 12 Mar 2025Fixed

The for loop contains repeated storage read access in the condition check. Given that the ending condition does not change in the for loop, the repeated storage read is unnecessary, and its associated high gas cost can be eliminated. ```solidity=156 for(uint i = 0; i < _pendingTransactions.length; i++) { ``` Loop condition `i < _pendingTransactions.length` accesses the `length` field of a storage array.

Open the report
LowUnnecessary Use of SafeMathMetacraft · CertiK · 12 Mar 2025Fixed

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.

Open the report
LowUnused Variables In `Transaction` StructureMetacraft · CertiK · 12 Mar 2025Fixed

The three variables `locked_`, `nodeCount_`, and `mintPrice_` in the `Transaction` structure appear to be unused in the contract. ```solidity struct Transaction { address withdrawAddress_; bool locked_; uint256 nodeCount_; uint256 mintPrice_; uint8 signatureCount; uint256 readyForExecutionTimestamp; uint256 timestamp; } ```

Open the report
LowUser May Experience Unexpected `mintPrice`Metacraft · CertiK · 12 Mar 2025Fixed

Given that `mintPrice` is variable, users might experience inconsistencies between the expected and actual `mintPrice` when utilizing the `buyNode()` function. In this situation, the `address(this)` directly transfers users' ERC20 tokens, potentially causing losses because users sometimes approve the contract to use `type(uint256).max` ERC20 tokens. ```solidity function buyNode( address _to, string memory tokenURI_, address parentAddress //uint256 tokenPrice ) public returns (uint256){ ... ERC20Token.safeTransferFrom(msg.sender, feeAddress, mintPrice); ... } ```

Open the report
LowVariables That Could Be Declared as ImmutableMetacraft · CertiK · 12 Mar 2025Fixed

The linked variable assigned in the constructor can be declared as `immutable`. Immutable state variables can be assigned during contract creation, but will remain constant throughout the lifetime of a deployed contract. A big advantage of immutable variables is that reading them is significantly cheaper than reading from regular state variables since they will not be stored in storage. ```solidity constructor( string memory _name, string memory _symbol, address _erc20Token, uint256 _mintPrice ) ERC721(_name, _symbol) { ... owner = msg.sender; ... } ```

Open the report
LowWrong dependency path for imported modulesMetacraft · CertiK · 12 Mar 2025Fixed

In version 0.5.0, OpenZeppelin made structural changes to its code base. Specifically, the [ReentrancyGuard contract](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/4551), initially located under the "security" path, has been relocated to the "utils" folder.

Open the report
Low`decimalsDiff` is assumed to be non-negativemumubit · CertiK · 21 Aug 2024Fixed

```c=518 let decimalsDiff: Int = self.projectCoinDecimals - self.investCoinDecimals; let adjustedAmount: Int = oldIdo.jettonIdoAmount * pow(10, decimalsDiff); ``` The code [assumes](https://docs.tact-lang.org/ref/core-math#pow) the `decimalsDiff` is non-negative, however, that is not guaranteed.

Open the report
Low`non_reentrant_lock` is redundantmumubit · CertiK · 21 Aug 2024Fixed

`non_reentrant_lock` is redundant. In TON during the processing of one message no external calls to another/same contract can be made. After the transaction execution the contract state changes are saved and prepared message queue is processed. Due to that using `lockContract()` at the message handler start and `unlockContract()` at the handler end is redundant. The execution can't be interrupted by other messages processing.

Open the report
Show 10 more
Low`Struct.fromSlice()` should be used instead of mamumubit · CertiK · 21 Aug 2024Fixed

[Struct.fromSlice()](https://docs.tact-lang.org/ref/core-cells#structfromslice) should be used instead of manual slice decoding.

Open the report
LowCompound assignment operators can be usedmumubit · CertiK · 21 Aug 2024Fixed

```c=179 self.index = (self.index + 1); ``` Compound assignment operators can be used for better clarity and efficiency. The statement `self.index += 1` achieves the same result but is more concise and idiomatic. This not only improves code readability but also reduces the likelihood of errors and enhances code maintainability by adhering to common programming conventions.

Open the report
LowEmpty `response_destination` is forbiddenmumubit · CertiK · 21 Aug 2024Fixed

[TEP-74 standard](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md) allows `response_destination` to be `addr_none (b00)`. In this case, the `excesses` message is not sent. However, the `TokenTransfer` declaration doesn't allow an empty address. This practically makes the argument non-optional, which can surprise the jetton users.

Open the report
LowRedundant codemumubit · CertiK · 21 Aug 2024Fixed

`TonClaim.projectCoinDecimals` is set during the contract deployment but never used. It can be arbitrary. The real project coin decimals should be obtained off-chain from the project coin master contract. `tonDecimals` is never used. `project_coin_address` can be arbitrary and can be changed by the `owner` at any time. `this_contract_projectCoinWallet` should be either derived from the `project_coin_address`, or the project coin address should be obtained off-chain from the wallet `get_jetton_data()` result. `MumubitIdo.raise_status` can be arbitrary.

Open the report
LowTyposmumubit · CertiK · 21 Aug 2024Fixed

```c=28 // 500 should be devided by 10000; ``` - "devided" is supposed to be "divided" - 10000 is supposed to be 1000 - "excludeed" is supposed to be "excluded"

Open the report
LowUnused code and leftoversmumubit · CertiK · 21 Aug 2024Fixed

There is redundant code and leftovers.

Open the report
LowVariable Can Be Declared as constantmumubit · CertiK · 21 Aug 2024Fixed

The variable `TonClaim.tonDecimals` is assigned but never changed. It can be declared as [constant](https://docs.tact-lang.org/book/constants).

Open the report
LowWrong wallet addressmumubit · CertiK · 21 Aug 2024Fixed

```c=80 self.invest_coin_address = investCoinAddress; self.this_contract_investCoinWallet = investCoinAddress; // let myInit= initOf JettonDefaultWallet(investCoinAddress,myAddress()); // self.this_contract_investCoinWallet = contractAddress(myInit); // self.investCoinDecimals = 6; // ton usdt 6 decimals self.project_coin_address = projectCoinAddress; self.this_contract_projectCoinWallet = projectCoinAddress; ``` `this_contract_investCoinWallet` and `this_contract_projectCoinWallet` are initialized with wrong addresses. They should be either derived from `investCoinAddress`/`projectCoinAddress`, or `investCoinAddress`/`projectCoinAddress` should be removed.

Open the report
LowInconsistency Between Code and Comment in `withdrMetacraft. · CertiK · 12 Apr 2024Fixed

In the `withdrawAllFunds()` function, there's an inconsistency between the implemented logic and the comment. The code enforces a condition that restricts fund withdrawal until 72 hours (3 days) have passed since the `claimTimestamp`. However, the comment misleadingly states a 48-hour (2 days) wait period. ```solidity=709 require(block.timestamp - claimTimestamp >= 3 days, "Withdraw Fund Until 48 hours after Claim "); ```

Open the report
LowInconsistency in `userLevel` Restrictions withinMetacraft. · CertiK · 12 Apr 2024Fixed

The `investInWithVerify()` function is designed to accept `userLevel` values of only 1, 2, and 3, aligning with specified access or participation levels. Despite this restriction, there are instances within the contract where `userLevel` 4 is used.

Open the report
Show 10 more
LowIncorrect CommentMetacraft. · CertiK · 12 Apr 2024Fixed

The following comment is outdated and incorrect since the contract logic has been changed. ```solidity=393 require(block.timestamp - transaction.timestamp >= 24 hours,"Time Lockin for 48 Hours for at Least 2 signers !"); ```

Open the report
LowIncorrect Time Lock Setting in `deleteMaxPendingTMetacraft. · CertiK · 12 Apr 2024Fixed

The `deleteMaxPendingTx()` function within the contract utilizes a time lock mechanism, `maxPendingTime`, intended to restrict the deletion of pending proposals until a certain period has passed. Currently, this time lock is set to 96 seconds, a duration significantly shorter than the intended design of 96 hours. This discrepancy could lead to premature deletion of pending proposals, potentially undermining the governance process or other sequential actions reliant on the time lock mechanism.

Open the report
LowIncorrect Update of `projectCoinAmount[projectCoiMetacraft. · CertiK · 12 Apr 2024Fixed

In the `withdrawAllFunds()` function, when the `tokenAddress_` parameter matches the `projectCoinAddress`, the `projectCoinAmount[projectCoinAddress]` captures the token balance before the token transfer, contrary to the intended behavior of recording the current token balance.

Open the report
LowIneffective `notContract()` CheckMetacraft. · CertiK · 12 Apr 2024Fixed

The implementation of the `notContract` check can not cover all scenarios. The check can be bypassed if the call is from the constructor of a smart contract or when the contract is destroyed. In that case, the codesize will also be zero. ```solidity modifier notContract(address _address) { uint256 codeSize; assembly { codeSize := extcodesize(_address) } require(codeSize == 0, "Contracts are not allowed to receive tokens"); _; } ```

Open the report
LowMissing checks for `ecrecover()` signature malleaMetacraft. · CertiK · 12 Apr 2024Fixed

`ecrecover()` accepts as valid, two versions of signatures, meaning an attacker can use the same signature twice, or an attacker may be able to front-run the original signer with the altered version of the signature, causing the signer's transaction to revert due to nonce reuse.

Open the report
LowPotential Cross-Chain Replay AttackMetacraft. · CertiK · 12 Apr 2024Fixed

A cross-chain replay attack is a type of malicious activity that occurs when a transaction or action intended for one blockchain is duplicated and executed on another blockchain, typically with unintended consequences or malicious intent. The contract is vulnerable to such attacks as it fails to validate signatures against the current chain ID, allowing attackers to exploit this on different chains. Additionally, when there is a major change in the blockchain, known as a "hard fork", it can split into two chains with different IDs. Using hardcoded or cached chain ID values can be risky because if the chain ID changes after the split, the signature that uses the previous chain ID will not work.

Open the report
LowPotential DoS Vulnerability Due to Iteration OverMetacraft. · CertiK · 12 Apr 2024Fixed

The `investInWithVerify()`, `refundOut()`, and `claimProjectCoin()` functions iterate over the `investors` array, which contains all investor information. Given that the array length can be increased by any address possessing the correct signature, these functions are exposed to potential denial-of-service (DoS) attacks. Malicious actors could excessively enlarge the `investors` array, leading to excessive gas costs or out-of-gas errors during function execution, thereby hindering contract operability.

Open the report
LowPotential Security Concern with Signature VerificMetacraft. · CertiK · 12 Apr 2024Fixed

The `setProjectInfo()` function allows modification of crucial variables before `startTimestamp`. The use of `publicKey`'s signature as a means of authentication allows any `msg.sender` with the correct signature to invoke this function. Given that other functions like `investInWithVerify()`, `refundOut()`, and `claimProjectCoin()` are restricted post-`startTimestamp` and considering normal users can obtain signatures, there's ambiguity around who can securely obtain and use the signature pre-`startTimestamp`. This raises security concerns about unauthorized alterations to project settings by entities other than the project owner.

Open the report
LowUnclaimed Tokens Can Be Withdrawn by `withdrawAddMetacraft. · CertiK · 12 Apr 2024Fixed

The `withdrawAllFunds()` function allows the `withdrawAddress` to withdraw the project coins from the contract. However, there's a risk that investors might not be able to claim their tokens if the `withdrawAddress` withdraws all project coins from the contract.

Open the report
LowAuction Status Not Check In `finishAuction`Metacraft. · CertiK · 5 Sep 2022Fixed

Auction status should be checked in the function `finishAuction`.

Open the report
Show 10 more
LowAuction Status Not Check In `isBidValid`Metacraft. · CertiK · 5 Sep 2022Fixed

Auction status is not checked in the function `isBidValid`. In this case, biding a canceled auction is allowed.

Open the report
LowImproper Usage of `public` and `external` TypeMetacraft. · CertiK · 5 Sep 2022Fixed

`public` functions that are never called by the contract could be declared as `external`. `external` functions are more efficient than `public` functions.

Open the report
LowMissing Check For `halted_`Metacraft. · CertiK · 5 Sep 2022Fixed

In the function `transfer()` of `MetaCraftToken`, it is noted that the contract checks the variable `halted_` first and then does the transfer. But in the function `transferFrom()` of `MetaCraftToken`, it missing the check for `halted_`. As a result, users can invoke the `approve()` and `transferFrom()` functions to work around the forbid of the calling on the `transfer()` function.

Open the report
LowRedundant CheckMetacraft. · CertiK · 5 Sep 2022Fixed

```solidity if(block.timestamp> auction.endTime) revert(); ``` The above check is already done in the function `isBidValid`.

Open the report
LowSafeMath Not UsedMetacraft. · CertiK · 5 Sep 2022Fixed

SafeMath from OpenZeppelin is not used in the following functions which makes them possible for overflow/underflow and will lead to an inaccurate calculation result. ```solidity payable(trade.poster).transfer(msg.value-perf); ``` ```solidity require(IERC20(tokens[symbol_]).approve(trade.poster, price_-perf), "Approve has failed"); IERC20(tokens[symbol_]).transferFrom(msg.sender, trade.poster, price_-perf); ```

Open the report
LowUnlocked compiler versionMetacraft. · CertiK · 5 Sep 2022Fixed

The contract has unlocked compiler version. An unlocked compiler version in the source code of the contract permits the user to compile it at or above a particular version. This, in turn, leads to differences in the generated bytecode between compilations due to different compiler versions. This can lead to an ambiguity when debugging as compiler specific bugs may occur in the codebase that would be hard to identify over a span of multiple compiler versions rather than a specific one.

Open the report
LowUnused VariableMetacraft. · CertiK · 5 Sep 2022Fixed

The variable `currToken` is declared but never used or updated.

Open the report
LowUnused VariableMetacraft. · CertiK · 5 Sep 2022Fixed

The variable `tradeCounter` and `currencyToken` are declared but never used or updated.

Open the report
LowUsage of `transfer()` for sending EtherMetacraft. · CertiK · 5 Sep 2022Fixed

After [EIP-1884](https://eips.ethereum.org/EIPS/eip-1884) was included in the Istanbul hard fork, it is not recommended to use `.transfer()` or `.send()` for transferring ether as these functions have a hard-coded value for gas costs making them obsolete as they are forwarding a fixed amount of gas, specifically `2300`. This can cause issues in case the linked statements are meant to be able to transfer funds to other contracts instead of EOAs.

Open the report
LowVariables that could be declared as `constant`Metacraft. · CertiK · 5 Sep 2022Fixed

The linked variables could be declared as `constant` since these state variables are never modified.

Open the report
Show 1 more
LowVariables That Could Be Declared as `constant`Metacraft. · CertiK · 5 Sep 2022Fixed

The linked variables could be declared as `constant` since these state variables are never modified.

Open the report

Contracts

No deployed address is recorded for these audits.

The reports name a repository or files. Open a report to see its scope.

The repositories and files the reports name

Show this on your site

Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.

Content

Facts

Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.

Fixed as
Language

Style

Layout
Theme
Accent
Corners

Format

Size
Date
Embed as
HTML
<head>
<script src="https://widget.trustblock.run/project-widget.min.js" defer></script>
</head>
<body>
<div
data-tb-project-widget
data-tb-project-slug="mumubit"
></div>
</body>

Paste the code where the label should appear. It uses no API credit and updates by itself.

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