Varun_05 - Whenever a new work is added to a existing edition it overrites the referrers[editon] value which denies the previous referrer from fees which he should recieve. #322
Whenever a new work is added to a existing edition it overrites the referrers[editon] value which denies the previous referrer from fees which he should recieve.
Summary
Whenever a new work is added to a existing edition it overrites the referrers[editon] value which denies the previous referrer from fees which he should receive.
Vulnerability Detail
Main function which is used to publish new edition or works is the internal _publish function
function _publish(Edition edition_, WorkPayload memory work_, address referrer_)
internal
returns (uint256 tokenId)
{
// Publish the new Work in the Edition
// wake-disable-next-line reentrancy
tokenId = edition_.publish(
work_.creator.target,
work_.maxSupply,
work_.opensAt,
work_.closesAt,
work_.attributions,
work_.strategy,
work_.metadata
);
// Collect the creation fee
// wake-disable-next-line reentrancy
feeManager.collectCreationFee{value: msg.value}(edition_, tokenId, msg.sender);
// Create the fee route for the new Work
// wake-disable-next-line reentrancy
Target memory feeReceiver = feeManager.createRoute(
edition_, tokenId, _attributionTargets(work_.attributions), referrer_
);
// Set the royalty target for the new Work
// wake-disable-next-line reentrancy
edition_.setRoyaltyTarget(tokenId, feeReceiver.target);
}
The following line of code sets the referrers for a particular edition whenever a new work is published
function createRoute(
IEdition edition_,
uint256 tokenId_,
Target[] calldata attributions_,
address referrer_
) external onlyOwnerOrRoles(ADMIN_ROLE) returns (Target memory receiver) {
Target memory creator = edition_.node(tokenId_).creator;
if (attributions_.length == 0) {
// No attributions, pay the creator directly
receiver = creator;
} else {
// Distribute the fee among the creator and attributions
(address[] memory targets, uint256[] memory revshares) = _buildSharesAndTargets(
creator, attributions_, edition_.feeStrategy(tokenId_).revshareBps
);
// Create the split. The protocol retains "ownership" to enable future use cases.
receiver = Target({
target: splitFactory.createSplit(
SplitV2Lib.Split({
recipients: targets,
allocations: revshares,
totalAllocation: 1e6,
distributionIncentive: 0
}),
address(this),
creator.target
),
chainId: creator.chainId
});
}
_feeReceivers[getRouteId(edition_, tokenId_)] = receiver;
referrers[edition_] = referrer_;
}
As can be seen from the following line referrers[edition] = referrer it always overrites the exisiting referrer to new referrer.
The issue here is whenever a new token is minted from the edition contract the user pays fees and some of that fees goes to the referrers[edition_] which is equal to the collectionReferrer share which can be seen from the following collectFee and splitprotocol fees in the fee manager contract
function _collectMintFee(
IEdition edition_,
uint256 tokenId_,
uint256 amount_,
address payer_,
address referrer_,
Fee memory fee_
) internal {
if (fee_.amount == 0) return;
// For free mints:
// - Protocol Share = 1/3 of flat fee
// - Edition Share = 2/3 of flat fee
//
// For priced mints:
// - Protocol Share = 100% of flat fee, shared as follows:
// - Edition Share = 100% of creator-specified mint cost, 0% of flat fee
//
// In both cases, the protocol and edition shares may be split as follows:
// - Protocol Share
// - If a referred mint, mint referrer gets 50% of the protocol share
// - If a referred collection, collection referrer gets 25% of the protcol share
// - Protocol fee receiver gets the remainder of the protocol share
// - Edition Share
// - Attributions equally split 25% of the edition share, if applicable
// - Creator gets the remainder of the edition share
uint256 protocolFee = protocolFlatFee * amount_;
uint256 protocolShare;
if (fee_.amount == protocolFee) {
protocolShare = protocolFee * protocolFeeshareBps / MAX_BPS;
} else {
protocolShare = protocolFee;
}
_route(
Fee({asset: fee_.asset, amount: fee_.amount - protocolShare}),
_feeReceivers[getRouteId(edition_, tokenId_)],
payer_
);
uint256 referrerShare =
_splitProtocolFee(edition_, fee_.asset, protocolShare, payer_, referrer_);
emit FeeCollected(address(edition_), tokenId_, fee_.asset, fee_.amount, referrerShare);
}
function _splitProtocolFee(
IEdition edition_,
address asset_,
uint256 amount_,
address payer_,
address referrer_
) internal returns (uint256 referrerShare) {
// The creation and mint referrers earn 25% and 50% of the protocol's share respectively, if applicable
uint256 mintReferrerShare = getMintReferrerShare(amount_, referrer_);
==> uint256 collectionReferrerShare = getCollectionReferrerShare(amount_, referrers[edition_]);
referrerShare = mintReferrerShare + collectionReferrerShare;
_route(
Fee({asset: asset_, amount: amount_ - referrerShare}),
Target({target: protocolFeeReceiver, chainId: block.chainid}),
payer_
);
_route(
Fee({asset: asset_, amount: mintReferrerShare}),
Target({target: referrer_, chainId: block.chainid}),
payer_
);
_route(
==> Fee({asset: asset_, amount: collectionReferrerShare}),
Target({target: referrer_, chainId: block.chainid}),
payer_
);
}
So if a new work is published then the previous referrer won't receive any fee as he would be overriden.It is a issue becasue hadn't new work been published then the previous referrer would have received his share of mint fee but now the referrer gets overridden everytime a new work is published.
Impact
An old referrer can be denied of his share of fee which he is applicable to receive. Here loss of funds is for the previous referres.
Don't what can be exact mitigation for this one , one can be to stores referrers[edition] as a mapping from a edition to an array of referrers which are added whenever a new work is published and distribute the collection referrer share fees to all of them equally.
Varun_05
high
Whenever a new work is added to a existing edition it overrites the referrers[editon] value which denies the previous referrer from fees which he should recieve.
Summary
Whenever a new work is added to a existing edition it overrites the referrers[editon] value which denies the previous referrer from fees which he should receive.
Vulnerability Detail
Main function which is used to publish new edition or works is the internal _publish function
The following line of code sets the referrers for a particular edition whenever a new work is published
CreateRoute function is as follows
As can be seen from the following line referrers[edition] = referrer it always overrites the exisiting referrer to new referrer. The issue here is whenever a new token is minted from the edition contract the user pays fees and some of that fees goes to the referrers[edition_] which is equal to the collectionReferrer share which can be seen from the following collectFee and splitprotocol fees in the fee manager contract
So if a new work is published then the previous referrer won't receive any fee as he would be overriden.It is a issue becasue hadn't new work been published then the previous referrer would have received his share of mint fee but now the referrer gets overridden everytime a new work is published.
Impact
An old referrer can be denied of his share of fee which he is applicable to receive. Here loss of funds is for the previous referres.
Code Snippet
https://github.com/sherlock-audit/2024-04-titles/blob/d7f60952df22da00b772db5d3a8272a988546089/wallflower-contract-v2/src/fees/FeeManager.sol#L159
Tool used
Manual Review
Recommendation
Don't what can be exact mitigation for this one , one can be to stores referrers[edition] as a mapping from a edition to an array of referrers which are added whenever a new work is published and distribute the collection referrer share fees to all of them equally.
Duplicate of #265