Tadle

Tadle
DeFiFoundry
27,750 USDC
View results
Submission Details
Severity: high
Valid

`updateReferrerInfo` function can only allow a self-referral to be created, which causes corresponding authority's real referrer to fail to earn any referral bonus when `createTaker` function is called

Summary

Because the updateReferrerInfo function only allows a self-referral to be created, the corresponding authority's real referrer cannot earn any referral bonus at all when the createTaker function is called.

Vulnerability Details

The following updateReferrerInfo function executes ReferralInfo storage referralInfo = referralInfoMap[_referrer] and referralInfo.referrer = _referrer. Since referralInfoMap[_referrer]'s referrer is such _referrer, calling the updateReferrerInfo function can only create a self-referral when msg.sender is not _referrer even though if (_msgSender() == _referrer) { revert InvalidReferrer(_referrer); } attempts to prevent a self-referral from being created. In other words, the updateReferrerInfo function cannot be used by the authority to set his referral to be someone who is not such authority.

https://github.com/Cyfrin/2024-08-tadle/blob/c249cdb68c37c47025cdc4c4782c8ee3f20a5b98/src/core/SystemConfig.sol#L41-L80

function updateReferrerInfo(
address _referrer,
uint256 _referrerRate,
uint256 _authorityRate
) external {
@> if (_msgSender() == _referrer) {
@> revert InvalidReferrer(_referrer);
}
if (_referrer == address(0x0)) {
revert Errors.ZeroAddress();
}
if (_referrerRate < baseReferralRate) {
revert InvalidReferrerRate(_referrerRate);
}
uint256 referralExtraRate = referralExtraRateMap[_referrer];
uint256 totalRate = baseReferralRate + referralExtraRate;
if (totalRate > Constants.REFERRAL_RATE_DECIMAL_SCALER) {
revert InvalidTotalRate(totalRate);
}
if (_referrerRate + _authorityRate != totalRate) {
revert InvalidRate(_referrerRate, _authorityRate, totalRate);
}
@> ReferralInfo storage referralInfo = referralInfoMap[_referrer];
@> referralInfo.referrer = _referrer;
referralInfo.referrerRate = _referrerRate;
referralInfo.authorityRate = _authorityRate;
...
}

When the createTaker function below is called, the referralInfo returned by systemConfig.getReferralInfo(_msgSender()) is the referralInfoMap[_referrer], where the _referrer is such msg.sender who is the authority. Yet, such referralInfoMap[_referrer]'s referrer would be the authority himself if the updateReferrerInfo function was called or such referralInfoMap[_referrer] would be empty if the updateReferrerInfo function was not called at all. Either way, none of the platformFee would be attributed to the authority's real referrer as such referrer's referral bonus when the _updateReferralBonus function below is called.

https://github.com/Cyfrin/2024-08-tadle/blob/c249cdb68c37c47025cdc4c4782c8ee3f20a5b98/src/core/SystemConfig.sol#L229-L233

function getReferralInfo(
address _referrer
) external view returns (ReferralInfo memory) {
return referralInfoMap[_referrer];
}

https://github.com/Cyfrin/2024-08-tadle/blob/c249cdb68c37c47025cdc4c4782c8ee3f20a5b98/src/core/PreMarkets.sol#L164-L284

function createTaker(address _offer, uint256 _points) external payable {
...
if (_points == 0x0) {
revert Errors.AmountIsZero();
}
OfferInfo storage offerInfo = offerInfoMap[_offer];
MakerInfo storage makerInfo = makerInfoMap[offerInfo.maker];
if (offerInfo.offerStatus != OfferStatus.Virgin) {
revert InvalidOfferStatus();
}
if (offerInfo.points < _points + offerInfo.usedPoints) {
revert NotEnoughPoints(
offerInfo.points,
offerInfo.usedPoints,
_points
);
}
...
ISystemConfig systemConfig = tadleFactory.getSystemConfig();
{
MarketPlaceInfo memory marketPlaceInfo = systemConfig
.getMarketPlaceInfo(makerInfo.marketPlace);
marketPlaceInfo.checkMarketPlaceStatus(
block.timestamp,
MarketPlaceStatus.Online
);
}
@> ReferralInfo memory referralInfo = systemConfig.getReferralInfo(
@> _msgSender()
);
uint256 platformFeeRate = systemConfig.getPlatformFeeRate(_msgSender());
...
address stockAddr = GenerateAddress.generateStockAddress(offerId);
if (stockInfoMap[stockAddr].authority != address(0x0)) {
revert StockAlreadyExist();
}
...
uint256 depositAmount = _points.mulDiv(
offerInfo.amount,
offerInfo.points,
Math.Rounding.Ceil
);
uint256 platformFee = depositAmount.mulDiv(
platformFeeRate,
Constants.PLATFORM_FEE_DECIMAL_SCALER
);
uint256 tradeTax = depositAmount.mulDiv(
makerInfo.eachTradeTax,
Constants.EACH_TRADE_TAX_DECIMAL_SCALER
);
ITokenManager tokenManager = tadleFactory.getTokenManager();
_depositTokenWhenCreateTaker(
platformFee,
depositAmount,
tradeTax,
makerInfo,
offerInfo,
tokenManager
);
offerInfo.usedPoints = offerInfo.usedPoints + _points;
...
stockInfoMap[stockAddr] = StockInfo({
id: offerId,
stockStatus: StockStatus.Initialized,
stockType: offerInfo.offerType == OfferType.Ask
? StockType.Bid
: StockType.Ask,
authority: _msgSender(),
maker: offerInfo.maker,
preOffer: _offer,
points: _points,
amount: depositAmount,
offer: address(0x0)
});
offerId = offerId + 1;
@> uint256 remainingPlatformFee = _updateReferralBonus(
platformFee,
depositAmount,
stockAddr,
makerInfo,
@> referralInfo,
tokenManager
);
makerInfo.platformFee = makerInfo.platformFee + remainingPlatformFee;
_updateTokenBalanceWhenCreateTaker(
_offer,
tradeTax,
depositAmount,
offerInfo,
makerInfo,
tokenManager
);
...
}

https://github.com/Cyfrin/2024-08-tadle/blob/c249cdb68c37c47025cdc4c4782c8ee3f20a5b98/src/core/PreMarkets.sol#L839-L904

function _updateReferralBonus(
uint256 platformFee,
uint256 depositAmount,
address stockAddr,
MakerInfo storage makerInfo,
ReferralInfo memory referralInfo,
ITokenManager tokenManager
) internal returns (uint256 remainingPlatformFee) {
if (referralInfo.referrer == address(0x0)) {
remainingPlatformFee = platformFee;
} else {
...
uint256 referrerReferralBonus = platformFee.mulDiv(
referralInfo.referrerRate,
Constants.REFERRAL_RATE_DECIMAL_SCALER,
Math.Rounding.Floor
);
...
tokenManager.addTokenBalance(
TokenBalanceType.ReferralBonus,
@> referralInfo.referrer,
makerInfo.tokenAddress,
referrerReferralBonus
);
uint256 authorityReferralBonus = platformFee.mulDiv(
referralInfo.authorityRate,
Constants.REFERRAL_RATE_DECIMAL_SCALER,
Math.Rounding.Floor
);
tokenManager.addTokenBalance(
TokenBalanceType.ReferralBonus,
_msgSender(),
makerInfo.tokenAddress,
authorityReferralBonus
);
remainingPlatformFee =
platformFee -
referrerReferralBonus -
authorityReferralBonus;
...
}
}

Impact

When the createTaker function is called, the corresponding authority's real referrer cannot earn any referral bonus at all.

Tools Used

Manual Review

Recommended Mitigation

https://github.com/Cyfrin/2024-08-tadle/blob/c249cdb68c37c47025cdc4c4782c8ee3f20a5b98/src/core/SystemConfig.sol#L69 can be updated to the following code.

ReferralInfo storage referralInfo = referralInfoMap[_msgSender()];
Updates

Lead Judging Commences

0xnevi Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-SystemConfig-updateReferrerInfo-msgSender

Valid high severity. There are two impacts here due to the wrong setting of the `refferalInfoMap` mapping. 1. Wrong refferal info is always set, so the refferal will always be delegated to the refferer address instead of the caller 2. Anybody can arbitrarily change the referrer and referrer rate of any user, resulting in gaming of the refferal system I prefer #1500 description the most, be cause it seems to be the only issue although without a poc to fully describe all of the possible impacts

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.