Closed code423n4 closed 1 year ago
I believe this is a design choice. Will leave it open for the sponsor's review for now but likely to invalidate.
hansfriese marked the issue as unsatisfactory: Insufficient quality
I believe this is a design choice. Will leave it open for the sponsor's review for now but likely to invalidate.
hey hans looks like the issue is closed, could it be re-opened so it can be reviewed by the sponsor?
Judge is right, fungibility of floor NFTs is a basic assumption. This swap is by design. Knowing this, rational lender should only bring floor asset (e.g., the NFT they get from collection bid).
However, we do appreciate the suggestion. We are considering a minimum fee in the interest to pay to the lender in order to prevent not-so-meaningful flash swap.
The third suggestion about Merkle tree and specified list is really smart! We will put this implementation in our roadmap as it supports trait level trading (restrict the fungibility to trait level, across trait still non-fungible).
wukong-particle marked the issue as disagree with severity
We acknowledge that this is a great suggestion (especially the third point), but it shouldn't be a high risk issue because the phenomenon does not deviate from the design.
wukong-particle marked the issue as sponsor acknowledged
Lines of code
https://github.com/code-423n4/2023-05-particle/blob/main/contracts/protocol/ParticleExchange.sol#L338 https://github.com/code-423n4/2023-05-particle/blob/main/contracts/protocol/ParticleExchange.sol#L466 https://github.com/code-423n4/2023-05-particle/blob/main/contracts/protocol/ParticleExchange.sol#L544
Vulnerability details
Particle Exchange can be used to swap NFTs within the collection for free
Impact
In Particle, a borrower can take an NFT as a loan and close it using another NTF within the same collection. This is present in the different functions that can be used to repay a loan,
buyNftFromMarket()
,repayWithNft()
orrefinanceLoan()
, that an arbitrarytokenId
to fulfill the loan. The logic is similar in all cases, but let's userepayWithNft()
as the example:https://github.com/code-423n4/2023-05-particle/blob/main/contracts/protocol/ParticleExchange.sol#L466-L515
Here we can see the loan being repaid by using any token from the collection. Line 487 updates the lien with the new
tokenId
while the NFT is transferred to the contract in line 499.While the intention is to let the borrower close the short by using any NFT from the collection, this can simply be used by the borrower to execute a swap for free. The borrower is allowed to take a particular NFT and return any other NFT from the same collection. Being non-fungible assets, it is expected that these have different prices according to rarity or certain particularities that make them have different valuations. This can even be done in a "flash swap" fashion, i.e. without paying any interest and without any liquidation risk if executed from a contract that would ensure atomicity of the different calls.
The corollary of the issue is that a lender will most likely always end up owning the cheapest NFT within the collection. Arbitrage and an efficient market will ensure this happens. From the point of view of the protocol this isn't too bad, a borrower can still short an NFT of a certain collection expecting price will fall (usually market movements affect the entire collection). But this will have a really high impact on lenders, as they will most surely lose value in their assets when the intention is to offer low risk yields (quoting the documentation, "NFT suppliers can earn customizable, low-risk yields").
Proof of concept
Recommendation
The quick solution would be to just make the borrower repay the loan using the same NFT, but this goes against a design decision and will most likely cripple the functionality of the exchange. If a borrower is trying to short the NFT and sells it, it would be unfeasible to think of making the borrower repurchase the same NFT to close the loan.
Another alternative would be to impose a fixed fee while taking the loans, or a minimum stay period, to ensure at least some earnings for the lender.
A third alternative, and the one I would recommend, is to allow the lender to specify a list of token ids that could be used to repay the loan. This would safely cover the lender knowing he can only receive one the approved NFTs. So if a lender decides to create a lien with an NFT of some category, they can build a list with all the other tokens from the same collection that have the same category (i.e. similar value or price). This can be implemented efficiently by using a Merkle tree, the lien can have the Merkle root and the functions can receive the path in the tree to prove the given token id belongs to the allowed list.
Assessed type
Other