Open code423n4 opened 2 years ago
The core concern here appears to be how numbers are attributed in events, which seems like a non-critical issue - however I argue below that it's correct as is.
Case B identifies a scenario that may not align with the creator's expectations. This could qualify as a Low risk issue.
This submission refers to risk to drops when assumePrimarySale
is not set, however it's hard-coded to true.
MarketFees
was in scope for this contest and in our other (out of scope) market implementation, assumePrimarySale
will be false -- so lets explore the submission from that point of view:
Case B is potentially confusing. It's possible a more appropriate action in this scenario is to check all the recipients and if the seller is found, attribute all the revenue to the recipients as we would have when normally sold by the creator.
For the events themselves - creatorRev
always represents how much the creator recipients received. This makes it easier to parse in scenarios where there is more than a single recipient. If we were to use sellerRev
instead as suggested in the rec, consumers would need to parse the sale scenario in order to determine if that amount was split amongst recipients or if it went to a single seller address.
I agree with the sponsor. Took me a while to understand the problem.
At first glance, it seems that the creator is better off with Case B: seller != creator
, but is one of the recipients. As the warden mentioned, "when the creator acts as the seller, he or she may recieve less than if someone who wasn't the creator acted as a seller for the same amount of revenue.". The rationale is because the creator gets paid twice, whereby he recieves his share in the creatorRecipients array, and also the sellerRev.
However, as the sponsor rightly points out, if we were to examine the amounts, it would actually be better for the seller to specify himself as the creator (ie. option A):
totalFees = price / PROTOCOL_FEE_DENOMINATOR; // 5%
creatorRev = price - totalFees; // 100% - 5% = 95%
creatorRev = price / CREATOR_ROYALTY_DENOMINATOR; // x% of 10% of price
sellerRev = price - totalFees - creatorRev; // 100% - 5% - 10% = 85%
Thus, since option A is incentivised, I agree that the creator is rightfully incentivised. Downgrading to low-severity QA because of Case B potentially being "a scenario that may not align with the creator's expectations".
Warden has no QA, marking as primary issue.
Lines of code
https://github.com/code-423n4/2022-08-foundation/blob/792e00df429b0df9ee5d909a0a5a6e72bd07cf79/contracts/mixins/shared/MarketFees.sol#L460-L474
Vulnerability details
[PNM-002] Revenue split inconsistency in
_getFees
Links
Description
_getFees
is an internal function used to designate how the revenue from selling an NFT drop collection is split. In all cases, revenue is given to two types of users:seller
(1) andcreatorRecipients
(many), usually, thecreator
is designated ascreatorRecipient[0]
.There exists multiple pathways that affect how the revenue is paid out, and in this scenario, assume that there is more than one
creatorRecipient
andassumePrimarySale
is false.There are then two possibilities: either the creator is the
seller
, or the creator is not theseller
, and these two options make up theelse if
andelse
branches on lines 460 and 467 respectively.If the creator is not the
seller
, or during theelse
branch, a new value is computed,sellerRev
representing the revenue earned by theseller
. However, this is not present in theelse if
branch, or when the creator is theseller
.In both cases, the remaining revenue is split amongst the
creatorRecipients
.In the
else
branch, if theseller
is also part of thecreatorRecipients
, he or she may be paid twice: first from thesellerRev
, and second from thecreatorRecipient
.However, in the
else if
branch, theseller
is only paid once by only thecreatorRecipient
share.This appears to be inconsistant, as when the
creator
acts as the seller, he or she may recieve less than if someone who wasn't thecreator
acted as a seller for the same amount of revenue.This can be taken advantage of by the
creator
using another account as aseller
to reap more profits from othercreatorRecipients
.It is important to note that the
seller
must add themselves to thecreatorRecipients
array; this is not done automatically.PoC / Attack Scenario
Case A (
creator
isseller
)creator
of an NFT drop collection with multiplecreatorRecipients
.creatorRecipients
array.Case B (
creator
is not theseller
)creator
of the NFT drop collection mentioned above, but he has aquired permision to sell it.creatorRecipients
array, and also thesellerRev
.Suggested Fix
Provide the
creator
with asellerRev
so any profits are transparent to other potentialcreatorRecipients
.