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
);
}
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
);
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;
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
);