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 {
@> 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.
function createTaker(address _offer, uint256 _points) external payable {
@> ReferralInfo memory referralInfo = systemConfig.getReferralInfo(
_msgSender()
);
offerId = offerId + 1;
@> uint256 remainingPlatformFee = _updateReferralBonus(
platformFee,
depositAmount,
stockAddr,
makerInfo,
@> referralInfo,
tokenManager
);
}
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 {
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
);
}
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
);
}