CertiK reviewed Lifty Gaming (Lifty) on 21 Apr 2023: 11 findings, 0 critical; 5 marked fixed, 5 acknowledged, 1 open.
Report hosted on skynet.certik.com.
Trustblock does not review code.
Statuses are the security firm’s own, from its report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 1 | 0 | 0 |
| Medium | 0 | 0 | 1 |
| Low | 0 | 5 | 4 |
When fees are applied to a transfer, the fees used are determined by the first asset in the `assets` array, which is the first bid asset in a sale and the first ask asset in an offer. ```solidity=54 transferPaymentAndFees( from, to, payment[i], assets[0], assets.length == 1, amount ); ``` This allows the order maker to arrange the bid and ask arrays such that the first token in each array has the lowest fees, resulting in possibly fewer fees collected by the protocol.
When processing an order, there is a check requiring proof that the order's hash is compatible with the root of a Merkle tree. ```solidity=125 require( order.signer == recoverSigner(order.rootSign, order.root), "ProceedOrder: wrong root sign" ); // check order merkle tree proof require( MerkleTree.verify(order.proof, order.root, orderHash), "ProceedOrder: invalid proof" ); ``` The root of the Merkle tree is also checked to be signed by `order.signer`. Since `order.amount` is not included in an order's hash, this allows the same order, with the same `order.root` and the same `order.rootSign`, but with a different `order.amount` to be processed multiple times.
In the function `changeMarketplaceCollectionFee()`, upper fee checks are implemented on the `marketplaceCollectionFee`. ```solidity=148 function changeMarketplaceCollectionFee( address collection, uint16 buyerFee, uint16 sellerFee ) external onlyOwner { require( sellerFee < 10000 && buyerFee < 10000, "FeeProvider: wrong fee amount" ); ``` --- However, for the *royalties* that are using `customCollectionRoyalties` the royalties are calculated using an external call `IGetRoyalties(collection)` without upper checks requirements. ```solidity=115 if (IERC165(collection).supportsInterface(INTERFACE_ID_FEES)) { IGetRoyalties collection = IGetRoyalties(collection); return (collection.getFeeRecipients(id), collection.getFeeBps(id)); } ```
The order type determines how fees are implemented, where the order maker pays fees for an `OFFER` and the order taker pays fees for a `SALE`. In the case of a `SWAP`, no fees are applied. Since the order type is not part of an order's hash, this allows the order taker to always state the order's type is a `SWAP` to avoid fees.
The function `buy()` is used to fill orders involving `ERC20`, `ERC721`, `ERC1155`, and native tokens. ```solidity=67 function buy( Order[] calldata orders, uint expiredAt, bytes calldata dealSign, bytes32[] calldata ordersHashes ) external payable nonReentrant { require(expiredAt > block.timestamp, "Buy: the deal is expired."); require( verifyDealSign(expiredAt, dealSign, ordersHashes), "Buy: wrong deal signature" ); for (uint256 i = 0; i < orders.length; ) { proceedOrder(orders[i], ordersHashes[i]); unchecked { ++i; } } } ``` Since there is no check that `msg.value` is the correct amount in the case of native token transfers, it is possible for native tokens to forever be locked within the contract.
| Address | Chain | Source code |
|---|---|---|
| 0xa4b7…a5290xa4b71b99eef99ea217adb7e3641cb3e779eaa529 | Chain: Ethereum | Source code: Verified source |
1,044 projects reviewed · 1,375 reports · latest May 2025
See CertiK’s auditsSomething 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