Tadle

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

Referral functionality doesn't works properly

Summary

According to current implementation the taker is passed as input to get the referrer thus it causes self referral which doesn't makes sense. It justs takes referral fees from the taker and at the end increases the balance of the taker itself thus nullifying any affect of referral fees.

Vulnerability Details

Following is createTaker function

function createTaker(address _offer, uint256 _points) external payable {
/**
* @dev offer must be virgin
* @dev points must be greater than 0
* @dev total points must be greater than used points plus _points
*/
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
);
}
/// @dev market place must be online
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());
/// @dev generate stock address
address stockAddr = GenerateAddress.generateStockAddress(offerId);
if (stockInfoMap[stockAddr].authority != address(0x0)) {
revert StockAlreadyExist();
}
/// @dev Transfer token from user to capital pool as collateral
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;
/// @dev update stock info
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
);
/// @dev emit CreateTaker
emit CreateTaker(
_offer,
msg.sender,
stockAddr,
_points,
depositAmount,
tradeTax,
remainingPlatformFee
);
}

Now as can be seen from the following lines in order to get referral info msg.sender i.e the taker is passed as input

ReferralInfo memory referralInfo = systemConfig.getReferralInfo(
_msgSender()
);

getReferralInfo function is as follows

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

Now we will see how referralInfoMap[_referrer] is set using the updateReferrerInfo function which is as follows

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;
emit UpdateReferrerInfo(
msg.sender,
_referrer,
_referrerRate,
_authorityRate
);
}

Key thing to note is the following how the mapping is set

ReferralInfo storage referralInfo = referralInfoMap[_referrer];
referralInfo.referrer = _referrer;
referralInfo.referrerRate = _referrerRate;
referralInfo.authorityRate = _authorityRate;

So referralInfoMap[referrer] has referrer as _referrer.

Now coming back to the create taker function

ReferralInfo memory referralInfo = systemConfig.getReferralInfo(
_msgSender()
);

So the above would return _msg.sender as the referrer if it exists.

Refferal fees is updated as follows

uint256 remainingPlatformFee = _updateReferralBonus(
platformFee,
depositAmount,
stockAddr,
makerInfo,
referralInfo,
tokenManager
);

_updateReferralBonus is as follows

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 {
/**
* @dev calculate referrer referral bonus and authority referral bonus
* @dev calculate remaining platform fee
* @dev remaining platform fee = platform fee - referrer referral bonus - authority referral bonus
* @dev referrer referral bonus = platform fee * referrer rate
* @dev authority referral bonus = platform fee * authority rate
* @dev emit ReferralBonus
*/
uint256 referrerReferralBonus = platformFee.mulDiv(
referralInfo.referrerRate,
Constants.REFERRAL_RATE_DECIMAL_SCALER,
Math.Rounding.Floor
);
/**
* @dev update referrer referral bonus
* @dev update authority referral bonus
*/
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;
/// @dev emit ReferralBonus
emit ReferralBonus(
stockAddr,
_msgSender(),
referralInfo.referrer,
authorityReferralBonus,
referrerReferralBonus,
depositAmount,
platformFee
);
}
}

Now issue is in most of the cases the taker won't be a referrer as referrer is some other person who send referrer links to the users in this case takers. So in most of the cases if condition would be executed and thus referral system wouldn't work as intended so even if some one referred the taker they wouldn't get any referral fees. Now suppose the taker itself is a referrar which then only else condition of the above function would be executed. The issue in that is first referral fees is transferred from the taker and then the token balance of the taker is increased thus nullyfing the effect as in both the following line of code referrer and msg.sender would be same i.e the taker.

tokenManager.addTokenBalance(
TokenBalanceType.ReferralBonus,
referralInfo.referrer,
makerInfo.tokenAddress,
referrerReferralBonus
);
tokenManager.addTokenBalance(
TokenBalanceType.ReferralBonus,
_msgSender(),
makerInfo.tokenAddress,
authorityReferralBonus
);

According to the docs referrerreferralBonus should be given to referrer and authorityReferralBonus should be given to the the taker. So according to the current implementation both are given to the taker so again incorrect implementation.

Impact

Referral system is broken.

Tools Used

Manual Review

Recommendations

Take referrer as input to the createTaker function which the taker will pass in and get refferal info for that referrer.

createTaker(address _offer, uint256 _points, address _referrer)
ReferralInfo memory referralInfo = systemConfig.getReferralInfo(
_referrer
);
Updates

Lead Judging Commences

0xnevi Lead Judge 12 months 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.