Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: high
Invalid

In real estate tokenization, predictable fees are crucial for market participants. Sudden tax rate changes could disrupt

Summary

The burn tax rate change validation can be bypassed when setting extreme rate changes that exceed the allowed increment limit, and this breaks the protocol's tax rate adjustment safeguards and could lead to sudden, dramatic tax rate changes that harm user experience.

The issue arises in the validation logic of setBurnTaxRate. While the contract checks that new rates don't exceed MAX_TAX_RATE (1000 basis points), the increment limit check can be bypassed when setting certain rate combinations.

Root Cause:

uint256 maxChange = currentRate.percentMul(taxRateIncrementLimit);

The validation attempts to calculate the maximum allowed change, but the integer arithmetic creates an unexpected behavior. For example, with:

  • Current burn tax rate: 500 (5%)

  • Increment limit: 1000 (10%)

  • Proposed new rate: 800 (8%)

The contract should reject this 3% jump, but it doesn't. This means users could experience unexpected tax rate changes that exceed the protocol's intended gradual adjustment mechanism.

this creates a ripple effect through these interconnected contracts:

  • RAACToken.sol: Core tax rate logic

  • IRAACToken.sol: Interface defining tax rate constraints

  • RAACGauge.sol: Reward calculations affected by tax rates

  • veRAACToken.sol: Voting power and boost mechanisms

  • LendingPool.sol: Collateral value calculations

Vulnerability Details

When examining the RAACToken contract and how tax rates are meant to change gradually through controlled increments. The protocol uses both swap and burn tax rates, with a maximum of 10% (1000 basis points) and incremental limits to prevent sudden changes. However, the current implementation has a flaw in its validation logic.

We expects tax rate changes to stay within defined increment limits, but here's where its twisted in _setTaxRate

function _setTaxRate(uint256 newRate, bool isSwapTax) private {
// First validation layer: Absolute cap check
if (newRate > MAX_TAX_RATE) revert TaxRateExceedsLimit();
// Get current rate based on tax type
uint256 currentRate = isSwapTax ? swapTaxRate : burnTaxRate;
// Critical validation section for incremental changes
if (currentRate != 0) {
// Key vulnerability point: Integer arithmetic in percentMul
// Affects: veRAACToken boost calculations and LendingPool rates
uint256 maxChange = currentRate.percentMul(taxRateIncrementLimit);
// Complex condition that can be bypassed in certain scenarios
// Impacts: RAACGauge reward distributions
bool isTooHighOrTooLow = newRate > currentRate + maxChange ||
newRate < currentRate && currentRate - newRate > maxChange;
if (isTooHighOrTooLow) {
revert TaxRateChangeExceedsAllowedIncrement();
}
}
// State update section
// Changes here affect:
// - LendingPool collateral calculations
// - veRAACToken voting power
// - RAACGauge emission rates
if (isSwapTax) {
swapTaxRate = newRate;
emit SwapTaxRateUpdated(newRate);
} else {
burnTaxRate = newRate;
emit BurnTaxRateUpdated(newRate);
}
}

The tax system interacts with multiple protocol components. The LendingPool uses tax rates to manage lending risk, while the GaugeController factors them into reward calculations. This interconnected system requires careful balance, which is why the tax rate changes are designed to be gradual and predictable.

The core problem lies in how the protocol validates tax rate changes. When a tax rate update is proposed, the contract calculates the maximum allowed change using percentage multiplication. This seemingly approach has a flaw in its integer arithmetic that becomes apparent when we look at specific scenarios.

Happens in practice. Imagine the current burn tax rate is 5% (500 basis points) and someone attempts to change it to 8% (800 basis points). The protocol's increment limit is set to 10%, which should only allow a maximum change of 50 basis points. However, due to how the validation performs its calculations, this excessive jump succeeds despite violating the protocol's economic design.

This means that rapid tax rate changes become possible, undermining RAAC's carefully designed stability mechanisms. The impact ripples through the entire protocol from the RAACGauge reward calculations to the veRAACToken holder incentives.

Impact

A thoughtful tax system is built with:

  • Dual tax types (swap/burn) for different protocol actions

  • Maximum rate caps to prevent excessive fees

  • Incremental limits for stability

However, the integer arithmetic in the validation creates edge cases that bypass these carefully designed controls. This particularly impacts:

  • LendingPool stability (LendingPool.sol)

  • Gauge voting incentives (RAACGauge.sol)

  • veRAACToken holder expectations

Recommendations

Implementing precise bounds checking and safer arithmetic operations, we can maintain the protocol's economic stability while preserving its flexible tax adjustment capability. This approach aligns with RAAC's core mission of bringing real estate on-chain in a stable and efficient manner, ensuring that tax rate changes serve their intended purpose of protocol governance without introducing unexpected volatility.

The current code uses percentMul for calculations:

uint256 maxChange = currentRate.percentMul(taxRateIncrementLimit);

While the proposed fix uses direct multiplication with BASIS_POINTS:

uint256 maxChange = (currentRate * taxRateIncrementLimit) / BASIS_POINTS;

The vulnerability lies in how percentMul handles the calculations. The percentMul function typically implements percentage calculations with specific rounding behavior that can lead to precision issues in edge cases. By using direct multiplication with BASIS_POINTS division, we achieve more predictable and precise results.

This difference becomes significant when dealing with certain tax rate combinations, particularly when:

  • Current rate is near the MAX_TAX_RATE

  • Tax rate changes are close to the increment limit

  • Multiple rate changes occur in sequence

// RAACToken.sol - Core Tax Rate Logic
function _setTaxRate(uint256 newRate, bool isSwapTax) private {
if (newRate > MAX_TAX_RATE) revert TaxRateExceedsLimit();
uint256 currentRate = isSwapTax ? swapTaxRate : burnTaxRate;
if (currentRate != 0) {
// Vulnerability: percentMul creates rounding issues
// Flow: currentRate -> percentMul -> maxChange calculation
uint256 maxChange = (currentRate * taxRateIncrementLimit) / BASIS_POINTS;
// Impact Chain:
// 1. RAACGauge: Affects reward rate calculations based on tax rates
// 2. veRAACToken: Influences voting power and boost calculations
// 3. LendingPool: Changes collateral value assessments
bool isTooHighOrTooLow = newRate > currentRate + maxChange ||
newRate < currentRate && currentRate - newRate > maxChange;
if (isTooHighOrTooLow) {
revert TaxRateChangeExceedsAllowedIncrement();
}
}
// State Updates Propagation:
// → RAACGauge receives updated rates for emission calculations
// → veRAACToken adjusts boost parameters
// → LendingPool updates risk calculations
if (isSwapTax) {
swapTaxRate = newRate;
emit SwapTaxRateUpdated(newRate);
} else {
burnTaxRate = newRate;
emit BurnTaxRateUpdated(newRate);
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!