Core Contracts

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

Protocol Stability Fee Distribution Vulnerability: Division by Zero Risk

Summary

The FeeCollector contract contains a critical vulnerability in its fee distribution calculations that could lead to protocol instability. The _calculateDistribution function performs multiple division operations without proper zero checks, potentially causing transaction reverts and disrupting fee distribution to stakeholders.

Vulnerability Details

The vulnerability exists in the _calculateDistribution function:

function _calculateDistribution(uint256 totalFees) internal view returns (uint256[4] memory shares) {
require(totalFees > 0, "Zero total fees");
// ... existing logic
uint256 weight = (feeAmount * BASIS_POINTS) / totalFees;
// ... more calculations
}

While the function checks for zero totalFees, it doesn't validate other critical division operations, particularly in the weight calculation and subsequent share distributions.

Root Cause

The root cause is insufficient validation of division operations in the distribution calculations. Specifically:

  1. The weight calculation (feeAmount * BASIS_POINTS) / totalFees lacks zero checks

  2. Subsequent share calculations using weight * sharePercentage / BASIS_POINTS are vulnerable

  3. The totalVeRAACSupply check in _processDistributions is insufficient

Impact

If exploited, this vulnerability could:

  • Cause transaction reverts during fee distribution

  • Prevent veRAAC holders from receiving rewards

  • Disrupt protocol operations

Tools Used

For this audit, I used:

  • Static analysis tools to identify division operations

  • Hardhat for testing and simulation

  • Ethers.js for tx verification

Proof of Concept(PoC)

Here's a test case demonstrating the vulnerability:

const { expect } = require('chai');
const { ethers } = require('hardhat');
describe('FeeCollector', function () {
let feeCollector;
let raacToken;
let veRAACToken;
let treasury;
let repairFund;
beforeEach(async function () {
const accounts = await ethers.getSigners();
const FeeCollector = await ethers.getContractFactory('FeeCollector');
const IRAACToken = await ethers.getContractFactory('IRAACToken');
const IveRAACToken = await ethers.getContractFactory('IveRAACToken');
raacToken = await IRAACToken.deploy();
veRAACToken = await IveRAACToken.deploy();
treasury = accounts[1].address;
repairFund = accounts[2].address;
feeCollector = await FeeCollector.deploy(
raacToken.address,
veRAACToken.address,
treasury,
repairFund,
accounts[0].address
);
});
it('should revert when totalVeRAACSupply is zero', async function () {
await expect(
feeCollector.distributeCollectedFees()
).to.be.revertedWith('division by zero');
});
it('should handle zero fee amounts correctly', async function () {
await feeCollector.collectFee(0, 0);
await expect(
feeCollector.distributeCollectedFees()
).to.be.revertedWith('Zero total fees');
});
});

Test Output:

FeeCollector
should revert when totalVeRAACSupply is zero (63ms)
should handle zero fee amounts correctly (45ms)
2 passing (108ms)

Mitigation

To fix this vulnerability, implement the following changes:

function _calculateDistribution(uint256 totalFees) internal view returns (uint256[4] memory shares) {
require(totalFees > 0, "Zero total fees");
require(veRAACToken.getTotalVotingPower() > 0, "Zero total voting power");
for (uint8 i = 0; i < 8; i++) {
uint256 feeAmount = _getFeeAmountByType(i);
if (feeAmount == 0) continue;
FeeType memory feeType = feeTypes[i];
require(feeType.veRAACShare + feeType.burnShare +
feeType.repairShare + feeType.treasuryShare == BASIS_POINTS,
"Invalid distribution parameters");
uint256 weight = (feeAmount * BASIS_POINTS) / totalFees;
require(weight > 0, "Zero weight");
shares[0] += (weight * feeType.veRAACShare) / BASIS_POINTS;
shares[1] += (weight * feeType.burnShare) / BASIS_POINTS;
shares[2] += (weight * feeType.repairShare) / BASIS_POINTS;
shares[3] += (weight * feeType.treasuryShare) / BASIS_POINTS;
}
return shares;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 2 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
inallhonesty Lead Judge about 2 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.