Wemix Retainer 123 PlayNftMarket

CertiKFiles named in the report

Audit

Part of CertiK’s retainer series

CertiK reviewed WEMIX (Wemix Retainer 123 PlayNftMarket) on 16 Dec 2024: 20 findings, 0 critical; 7 marked fixed, 13 open.

No open critical findings
Open the reportOpen WEMIX’s security record

Report hosted on skynet.certik.com.

Trustblock does not review code.

Findings20

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

SeverityOpenAcknowledgedFixed
Critical000
High202
Medium103
Low1002
HighMissing access control in `cancel()` function allFixed

The `cancel()` function in the `PlayMarketOfferBiddingOrder` contract is intended to be called by the market to allow users to cancel their orders. However, this function is missing the `onlyMarket` access control modifier. This vulnerability allows malicious actors to directly call the `cancel()` function in the `PlayMarketOfferBiddingOrder` contract, enabling them to cancel orders on behalf of other users without their permission.

HighUnable to buy orders due to inconsistent usage ofFixed

In `offerSellOrder()` function, the meaning of the `amount` parameter varying based on `order.orderStrategyType`: - For `order.orderStrategyType == Type.OrderStrategyType.SELL`, buyers need to pay `orderPrice = _order.price * amount`, where `amount` represents the number of NFTs to purchase (`PlayMarketFixedPriceOrder#L52`). - For `order.orderStrategyType == Type.OrderStrategyType.OFFER_BIDDING`, buyers need to pay `amount` of payment tokens, where `amount` represents the order price. To ensure sufficient payment in the second case, the function performs the following check: ```solidity=277 require(amount >= _order.price, "MB: insufficient amount" ); ``` However, this check breaks the first case where `amount` represents the number of NFTs, resulting in buyers not being able to call the `offerSellOrder()` function to buy NFTs if `order.orderStrategyType == Type.OrderStrategyType.SELL`. Additionally, for `OFFER_BIDDING` orders, buyers should be allowed to increase their offered price by any amount, rather than incrementing by `order.price`.

MediumIncompatible Payment Token Handling in `PlayMarkeFixed

The `PlayMarketFixedPriceOrderV2` contract has been updated to inherit from `PlayMarketOrderBaseV3`, which mandates using WWEMIX as the payment token for non-ERC20 payment orders. In the `offer` function of `PlayMarketFixedPriceOrderV2`, the contract checks if the payment token is `address(0)`, implying a transfer of Ether: ```solidity=53 if (_order.payment == address(0)) { require(msg.value == orderPrice, "FP : Invalid value"); } else { IERC20(_order.payment).safeTransferFrom(payer, address(this), orderPrice); } ``` The check does not appropriately handle WWEMIX, as WWEMIX is a wrapped version of WEMIX and should not be recognized as `address(0)`. Furthermore, the internal functions `_nftOffer()` and `_bundleOffer()` fail to unwrap WWEMIX before invoking `_transferToken()`. Consequently, orders with WWEMIX payment cannot be processed correctly, leading to payment failures.

MediumIncorrect Token Balance Check for WWEMIX TokensFixed

In the process of wrapping WEMIX tokens to WWEMIX tokens, the `Checker::checkBalance()` function attempts to verify the strategy contract's token balance. This function checks the balance for a specified token in order to ensure the contract received the correct amount. As shown in the code snippet below, if the token is denoted by `address(0)`, the function checks the ETH balance of the contract using `address(this).balance`: ```solidity=59 function checkBalance(address token, uint value) internal view returns (bool) { uint balance; if (token == address(0)) { balance = address(this).balance; } else { balance = IERC20(token).balanceOf(address(this)); } return balance >= value; } ``` However, since the strategy contract receives WWEMIX tokens rather than the native ETH in the new commit a4c547d, checking the balance of `address(this).balance` when the payment token is `address(0)` will not reflect the WWEMIX token balance, leading to a transaction revert.

MediumUnintended cancellation of accepted offersFixed

Users can cancel their own unaccepted offers to refund their payments. However, this can unintentionally lead to the cancellation of an accepted offer in the following scenario: 1. A seller accepts an offer, updating the `order.orderStatus` to `ACCEPTED`. 2. The owner of the accepted offer does not claim the order immediately, leaving the order status as `ACCEPTED`. 3. Another user cancels their own unaccepted offer, triggering the following code snippet: ```solidity=250 if (_order.orderStatus == Type.OrderStatus.ACCEPTED) { _cancelAccept(_order); } ``` This logic unintentionally cancels the accepted offer when it should only affect the unaccepted offer being cancelled.

LowConcerns about `PlayNFTMarketOracle`Fixed

If `priceOracle` is not a zero address, the `offerBuyOrder()` function will store NFT price of the completed order. However, the `claimOrder()` function, which marks the `OFFER_BIDDING` order as completed, does not save the NFT price to the `priceOracle` contract.

LowInconsistent usage of `_registeredOnOfferBidding`Fixed

The `_registeredOnOfferBidding` mapping tracks `OFFER_BIDDING` orders and prevents the registration of other orders for the same NFT when it is registered for an `OFFER_BIDDING` order. However, there are inconsistent issues regarding the usage of this mapping: - The mapping also tracks `SELL` and `AUCTION` orders. - Provide offer for `SELL` orders will not remove the order ID from `_registeredOnOfferBidding` mapping. - Claim order for `AUCTION` orders will not remove the order ID from `_registeredOnOfferBidding` mapping.

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

  • contracts/order/PlayMarketOfferBiddingOrderV2.sol
  • contracts/library/Orders.sol
  • contracts/manager/PlayMarketTokenmanagerV4.sol
  • contracts/market/PlayMarketBaseV5.sol
  • contracts/market/PlayMarketBaseV6.sol
  • contracts/order/PlayMarketOfferBiddingOrder.sol
  • contracts/interface/IOrderV2.sol

Part of WEMIX’s security record

WEMIX
Audits75 audits by 1 firmLast audit Feb 2025, by CertiK.
Critical findingsNo open critical findingsAcross the 75 reports, 17 critical findings were reported. Each is marked fixed by the firm that reported it.
Open WEMIX’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