Summary
In RAACToken.sol, the swapTaxRate
and burnTaxRate
is assumed to be able to set to zero from the _update()
function:
uint256 baseTax = swapTaxRate + burnTaxRate;
if (baseTax == 0 ...
However it is not possible to set swapTaxRate
or burnTaxRate
to zero because of the taxRateIncrementLimit
which is limited to 1000 (10%).
Vulnerability Details
When changing the taxRates, the owner has to call _setTaxRate()
with the newRate. The newRate must be within the taxRateIncrementLimit
bounds of the oldTaxRate.
For example, if the swap tax is at 500 (5%) and taxRateIncrement is at 1000 (10%), then the new swap tax can only be within 90-110% of the current 500, so within ~450-550.
function _setTaxRate(uint256 newRate, bool isSwapTax) private {
if (newRate > MAX_TAX_RATE) revert TaxRateExceedsLimit();
uint256 currentRate = isSwapTax ? swapTaxRate : burnTaxRate;
if (currentRate != 0) {
uint256 maxChange = currentRate.percentMul(taxRateIncrementLimit);
>
bool isTooHighOrTooLow = newRate > currentRate + maxChange || newRate < currentRate && currentRate - newRate > maxChange;
if (isTooHighOrTooLow) {
revert TaxRateChangeExceedsAllowedIncrement();
}
}
if (isSwapTax) {
swapTaxRate = newRate;
emit SwapTaxRateUpdated(newRate);
} else {
burnTaxRate = newRate;
emit BurnTaxRateUpdated(newRate);
}
}
This means that the tax rate cannot be set < 9, since the transaction will revert.
Here is a Remix test, set the taxRateIncrementLimit
to 1000, then set the swapTax to any amount. Note that once it reaches 9, it cannot get lower or higher anymore:
pragma solidity ^0.8.0;
contract TaxRateManager {
uint256 public constant MAX_TAX_RATE = 1000;
uint256 public taxRateIncrementLimit;
uint256 public swapTaxRate;
uint256 public burnTaxRate;
event SwapTaxRateUpdated(uint256 newRate);
event BurnTaxRateUpdated(uint256 newRate);
error TaxRateExceedsLimit();
error TaxRateChangeExceedsAllowedIncrement();
function _setTaxRate(uint256 newRate, bool isSwapTax) private {
if (newRate > MAX_TAX_RATE) revert TaxRateExceedsLimit();
uint256 currentRate = isSwapTax ? swapTaxRate : burnTaxRate;
if (currentRate != 0) {
uint256 maxChange = _percentMul(currentRate, taxRateIncrementLimit);
bool isTooHighOrTooLow = newRate > currentRate + maxChange || (newRate < currentRate && currentRate - newRate > maxChange);
if (isTooHighOrTooLow) {
revert TaxRateChangeExceedsAllowedIncrement();
}
}
if (isSwapTax) {
swapTaxRate = newRate;
emit SwapTaxRateUpdated(newRate);
} else {
burnTaxRate = newRate;
emit BurnTaxRateUpdated(newRate);
}
}
function updateSwapTaxRate(uint256 newRate) external {
_setTaxRate(newRate, true);
}
function updateBurnTaxRate(uint256 newRate) external {
_setTaxRate(newRate, false);
}
function updateIncrement(uint256 _taxRateIncrementLimit) external {
taxRateIncrementLimit = _taxRateIncrementLimit;
}
function _percentMul(uint256 value, uint256 percent) private pure returns (uint256) {
return (value * percent) / 10000;
}
}
Impact
Tax cannot be set at 0%
Tools Used
Manual Review
Recommendations
Allow the function to accept 0 as the new tax, and if the owner wants to set the tax again he can choose any arbitrary amount.
function _setTaxRate(uint256 newRate, bool isSwapTax) private {
if (newRate > MAX_TAX_RATE) revert TaxRateExceedsLimit();
> if (newRate == 0) {
if (isSwapTax) {
swapTaxRate = newRate;
emit SwapTaxRateUpdated(newRate);
return;
} else {
burnTaxRate = newRate;
emit BurnTaxRateUpdated(newRate);
return;
}
}
...