Quantstamp reviewed Particle on 23 Aug 2023: 20 findings, 0 critical; 8 marked fixed, 12 acknowledged.
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 | 1 |
| Medium | 0 | 0 | 3 |
| Low | 0 | 12 | 4 |
There are several design assumptions in the system: 1. The interest is not bounded; The maximum APY is 1000%. 2. The market buy process and the auction process will revert if payback to borrower < 0. As a result, the lender will never receive interest higher than the margin. This is not clearly documented in the user-facing documentation. Also, there is no notification when the accrued interest is higher than the margin.
It is crucial to address the risks associated with the absence of whitelisting the NFT collection address in the `_supplyNf()` function. Failure to whitelist the collection can potentially render the contract vulnerable malicious user actions, especially if the collections contain exploits or have non-standard ERC721 implementations. This is particularly concerning because users have the freedom to select any collection address.
The clone-and-own approach involves copying and adjusting open source code at one's own discretion. From the development perspective, it is initially beneficial as it reduces the amount of effort. However, from the security perspective, it involves some risks as the code may not follow the best practices, may contain a security vulnerability, or may include intentionally or unintentionally modified upstream libraries.
The following validation checks are missing from `ParticelExchange`: 1. `supplyNft() `: Validate that `price` is greater than zero. 2. `updateLoan() `: Validate that `price` is greater than zero. 3. `sellNftToMarketPull() `: Validate that `amount` is greater than zero. 4. `sellNftToMarketPush() `: Validate that `amount` is greater than zero. 5. `acceptBidSellNftToMarketPull() `: Validate that `amount` is greater than zero. 6. `acceptBidSellNftToMarketPush() `: Validate that `amount` is greater than zero. 7. `registerMarketplace()`: Ensure that `marketplace` is not the zero address. 8. `constructor` should validate the input address `wethAddress` is not 0.
The following value in the contract is returned without validation: - `weth.approve(spender, amount);` in function `_execBuyNftFromMarket()`
The `ParticleExchange` contract is an upgradeable contract and should inherit from the upgradeable version of OpenZeppelin's contracts. However, `ParticleExchange` inherits from `ReentrancyGuard` instead of `ReentrancyGuardUpgradeable`. `ReentrancyGuardUpgradeable` has features that support upgradeability, such as a storage gap and a chained initialization function.
If the owner renounces their ownership, all ownable contracts will be left without an owner. Consequently, any function guarded by the `onlyOwner` modifier will no longer be able to be executed. The `ParticelExchange` contract currently relies on an owner for improtant administrative functionaltiy.
The `ParticleExchange` contract features an owner who has exclusive permission to access the administrative functionality of the contract. It is important for project users to be aware of the privileged roles and their permissions so that they are aware of all potential risks. We have listed the privileged permissions of the contract owner below: 1. Register marketplaces that can be used to buy or sell NFTs through the protocol: `registerMarketplace()`. 2. Unregister a marketplace so that it can no longer be used to buy or sell NFTs through the protocol: `unregisterMarketplace()`. 3. Set the percentage of the interest fee to be paid to the protocol with a maximum of `10%`: `setTreasuryRate()`. 4. Withdraw funds from the treasury to an arbitrary address: `withdrawTreasury()`. 5. Allow the owner to upgrade the contract: `_authorizeUpgrade()`.
A supplier may lend an NFT to the protocol, and it may stay unborrowed indefinitely. A worse scenario would be that the NFT does not get borrowed for a long time and then gets borrowed at market conditions that are unfavorable for the supplier. Since the protocol has no protection through oracles, it is important to implement an expiry time such that if a supplied NFT is not borrowed for a certain amount of time, it can not be borrowed anymore. The same scenario applies to bids.
The `ParticleExchange` contract is upgradeable. While this is not a vulnerability, users should be aware that the behavior of the contracts could change if the contracts are upgraded. Furthermore, new vulnerabilities not present during the audit could be introduced in upgraded versions of the contract.
The `ParticleExchange` contract does not rely on any oracles, so only suppliers can be relied on to activate liquidations in the form of auctions. The supplier must monitor the price of the NFT to ensure that the trader's margin has not been used up. Similarly, the trader must monitor when the supplier starts the auctions so that they can refinance if they don't want their position closed prematurely.
In function `updateBid` and `offerBid`, the new lien is created with: ```solidity Lien memory lien = Lien({ lender: address(0), borrower: msg.sender, collection: collection, tokenId: margin, /// @dev: use tokenId for margin storage price: price, rate: rate, loanStartTime: 0, auctionStartTime: 0 }); ``` While the code logic is correct, the mapping `liens` is a public mapping. It is unknown if such information will be displayed through other interfaces (such as web), as the users cannot identify the differences between `tokenId` and `margin`. Since `tokenId` and `margin` should be significantly different in number of digits, we consider this as a low risk issue.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (6)
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