Tadle

Tadle
DeFi
30,000 USDC
View results
Submission Details
Severity: high
Valid

Implementations of `ReferralInfo` may not work as expected.

Summary

Implementations of ReferralInfo may not work as expected.

Vulnerability Details

Administrators update referral extra rates via SystemConfig::updateReferralExtraRateMap().

function updateReferralExtraRateMap(
address _referrer,
uint256 _extraRate
) external onlyOwner {
uint256 totalRate = _extraRate + baseReferralRate;
if (totalRate > Constants.REFERRAL_RATE_DECIMAL_SCALER) {
revert InvalidTotalRate(totalRate);
}
@> referralExtraRateMap[_referrer] = _extraRate;
emit UpdateReferralExtraRateMap(_referrer, _extraRate);
}

when referralExtraRateMap[_referrer] > 0, anyone other than _referrer can call SystemConfig::updateReferrerInfo() to update the referrer setting. Note that referralInfo.referrer = _referrer.

function updateReferrerInfo(
address _referrer,
uint256 _referrerRate,
uint256 _authorityRate
) external {
// SNIP...
@> 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
);
}

When a user calls PreMarkets::createTaker() to create a Taker, ReferralInfo is obtained through systemConfig.getReferralInfo(_msgSender()). Obviously, referralInfo.referrer == _msgSender() is used here. Then PreMarkets::_updateReferralBonus() is called to update the referral bonus.

// PreMarkets::createTaker()
function createTaker(address _offer, uint256 _points) external payable {
// SNIP...
@> ReferralInfo memory referralInfo = systemConfig.getReferralInfo(
_msgSender()
);
// SNIP...
offerId = offerId + 1;
@> uint256 remainingPlatformFee = _updateReferralBonus(
platformFee,
depositAmount,
stockAddr,
makerInfo,
@> referralInfo,
tokenManager
);
// SNIP...
}
// SystemConfig::getReferralInfo()
function getReferralInfo(
address _referrer
) external view returns (ReferralInfo memory) {
@> return referralInfoMap[_referrer];
}

However, we noticed that TokenManager::addTokenBalance() was called twice in the PreMarkets::_updateReferralBonus() function to update the balance. If referralInfo.referrer == _msgSender() is expected, the balance should not be updated in this repeated manner. Combined with the relevant content of event ReferralBonus(), it is inferred that the relevant code implementation may not be consistent with expectations.

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 {
// SNIP...
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
);
}
/// @dev Event when referrer updated
event ReferralBonus(
address indexed stock,
@> address authority,
@> address referrer,
uint256 authorityReferralBonus,
uint256 referrerReferralBonus,
uint256 tradingVolume,
uint256 tradingFee
);

Code Snippet

https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/core/SystemConfig.sol#L199-L209
https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/core/SystemConfig.sol#L41-L80
https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/core/PreMarkets.sol#L164-L284
https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/core/SystemConfig.sol#L228-L233

Impact

Implementations of ReferralInfo may not work as expected.

Tools Used

Manual Review

Recommendations

Check and modify ReferralInfo related code.

I guess the problem may exist in the SystemConfig::updateReferrerInfo() function:
Consider the following method modification, which can ensure the permission restrictions of function calls and make the code work as expected.

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 referralExtraRate = referralExtraRateMap[_msgSender()];
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 storage referralInfo = referralInfoMap[_msgSender()];
referralInfo.referrer = _referrer;
referralInfo.referrerRate = _referrerRate;
referralInfo.authorityRate = _authorityRate;
emit UpdateReferrerInfo(
msg.sender,
_referrer,
_referrerRate,
_authorityRate
);
}
Updates

Lead Judging Commences

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