Quantstamp reviewed Talentir Token & Marketplace on 11 Apr 2023: 19 findings, 0 critical; 11 marked fixed, 5 acknowledged, 3 open.
Report hosted on certificate.quantstamp.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 | 0 | 0 | 2 |
| Medium | 2 | 0 | 0 |
| Low | 1 | 5 | 9 |
When the marketplace address is changed in `TalentirTokenV1.setMarketplace()` only newly minted tokens will have it approved for all, while previously minted tokens will still have the old marketplace address approved for all. This would lead the tokens to be at risk of token owners of previously minted tokens until they manually revoke/update the approval via `setApprovalForall()`.
A hacker may submit many tiny orders, either cheap sell orders or very expensive buy orders. In doing so, they would guarantee that these small orders would be executed first. If there is a sufficient number of these small orders, then normal buy or sell orders would run out of gas before they fill all the small orders. We will explain the problem of having many tiny expensive buy orders. However, the corresponding problem with sell orders is almost the same. The `TalentirMarketplaceV1` allows users to either buy Talentir tokens with the chain's native currency, such as ETH or sell Talentir tokens in exchange for the chain's native currency. If a user submits a sell order, `TalentirMarketplaceV1` first tries to find the most expensive buy order to fill the sell order with. Thus, if a malicious buyer is able to submit many tiny buy orders with the highest price, these orders are always guaranteed to be used to fill the next sell order. This can be seen in the function which is used to retrieve the appropriate buy order: ``` function getBestOrder(uint256 _tokenId, Side _side) public view returns (uint256, uint256) { uint256 price = _side == Side.BUY ? markets[_tokenId][_side].priceTree.last() : markets[_tokenId][_side].priceTree.first(); uint256 bestOrderId; (, bestOrderId, ) = markets[_tokenId][_side].orderList[price].getNode(0); return (bestOrderId, price); } ``` The function for executing a normal sell order will loop over the buy orders to fill it, starting from the most expensive buy order. Thus if the malicious user places enough expensive buy orders, this function is forced to loop through all of them, potentially reaching the gas limit and failing: ``` function _makeOrder( address _sender, uint256 _tokenId, Side _side, uint256 _ethQuantity, uint256 _tokenQuantity, bool _addOrderForRemaining ) internal { ... (bestOrderId, bestPrice) = getBestOrder(_tokenId, oppositeSide); // If possible, buy up to the specified price limit uint256 remainingQuantity = _tokenQuantity; while ( (remainingQuantity > 0) && ((_side == Side.BUY) ? price >= bestPrice : price <= bestPrice) && (bestOrderId > 0) ) { ... if (remainingQuantity > 0) { (bestOrderId, bestPrice) = getBestOrder(_tokenId, oppositeSide); } } ... } ```
Gas usage is a main concern for smart contract developers and users, since high gas costs may prevent users from wanting to use the smart contract. For example, if a loop requires too much gas to finish processing, then it may prevent the contract from functioning correctly entirely. When creating an order the while loop in `_makeOrder()` could run out of gas if there is a very large amount of orders on the opposite side. Even in the case where the orders are benign, some gas concerns may occur.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (2)
236 projects reviewed · 332 reports · latest Oct 2026
See Quantstamp’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