Summary
The RAACToken.sol contract allows functions to be called by the owner instead of the intended RAACMinter.sol minter.
Vulnerability Details
There are certain permissioned functions inside RAACToken.sol that should only be called by RAACMinter.sol (via the onlyMinter modifier), such as the mint function:
function mint(address to, uint256 amount) external onlyMinter {
if (to == address(0)) revert InvalidAddress();
_mint(to, amount);
}
However, there are other functions within RAACToken.sol that are also being called from RAACMinter.sol, like setSwapTaxRate:
function setSwapTaxRate(uint256 _swapTaxRate) external onlyRole(UPDATER_ROLE) {
if (_swapTaxRate > 1000) revert SwapTaxRateExceedsLimit();
=> raacToken.setSwapTaxRate(_swapTaxRate);
emit ParameterUpdated("swapTaxRate", _swapTaxRate);
}
setBurnTaxRate:
function setBurnTaxRate(uint256 _burnTaxRate) external onlyRole(UPDATER_ROLE) {
if (_burnTaxRate > 1000) revert BurnTaxRateExceedsLimit();
=> raacToken.setBurnTaxRate(_burnTaxRate);
emit ParameterUpdated("burnTaxRate", _burnTaxRate);
}
And setFeeCollector:
function setFeeCollector(address _feeCollector) external onlyRole(UPDATER_ROLE) {
if (_feeCollector == address(0)) revert FeeCollectorCannotBeZeroAddress();
=> raacToken.setFeeCollector(_feeCollector);
emit ParameterUpdated("feeCollector", uint256(uint160(_feeCollector)));
}
However, upon inspecting RAACToken.sol, we can see that these three functions (setSwapTaxRate, setBurnTaxRate, and setFeeCollector) can only be called by the owner of the contract, not the minter (which is RAACMinter.sol):
* @dev Sets the fee collector address
* @param _feeCollector The address of the new fee collector
*/
=> function setFeeCollector(address _feeCollector) external onlyOwner {
if(feeCollector == address(0) && _feeCollector != address(0)){
emit FeeCollectionEnabled(_feeCollector);
}
if (_feeCollector == address(0)){
emit FeeCollectionDisabled();
}
feeCollector = _feeCollector;
emit FeeCollectorSet(_feeCollector);
}
* @dev Sets the swap tax rate
* @param rate The new swap tax rate (in basis points)
*/
=> function setSwapTaxRate(uint256 rate) external onlyOwner { _setTaxRate(rate, true); }
* @dev Sets the burn tax rate
* @param rate The new burn tax rate (in basis points)
*/
=> function setBurnTaxRate(uint256 rate) external onlyOwner { _setTaxRate(rate, false); }
Impact
setSwapTaxRate, setBurnTaxRate, and setFeeCollector are not callable by RAACMinter.
Tools Used
Manual Review
Recommendations
/**
* @dev Sets the fee collector address
* @param _feeCollector The address of the new fee collector
*/
- function setFeeCollector(address _feeCollector) external onlyOwner {
+ function setFeeCollector(address _feeCollector) external onlyMinter {
// Fee collector can be set to zero address to disable fee collection
if(feeCollector == address(0) && _feeCollector != address(0)){
emit FeeCollectionEnabled(_feeCollector);
}
if (_feeCollector == address(0)){
emit FeeCollectionDisabled();
}
feeCollector = _feeCollector;
emit FeeCollectorSet(_feeCollector);
}
/**
* @dev Sets the swap tax rate
* @param rate The new swap tax rate (in basis points)
*/
- function setSwapTaxRate(uint256 rate) external onlyOwner { _setTaxRate(rate, true); }
+ function setSwapTaxRate(uint256 rate) external onlyMinter { _setTaxRate(rate, true); }
/**
* @dev Sets the burn tax rate
* @param rate The new burn tax rate (in basis points)
*/
- function setBurnTaxRate(uint256 rate) external onlyOwner { _setTaxRate(rate, false); }
+ function setBurnTaxRate(uint256 rate) external onlyMinter { _setTaxRate(rate, false); }