The RAACToken::_update function does not correctly account for burn tax distribution, leading to the feeCollector receiving a lower-than-expected amount. The issue arises due to incorrect handling of tax calculations and improper sequencing of _update calls.
According to documenation:
Implements a burn function with a separate burn tax mechanism
The RAACToken::burn function is expected to apply a burn tax based on the user’s desired amount multiplied by the burnTaxRate. However, the way _update processes the tax distribution causes the feeCollector to receive less than the expected burn tax amount.
The RAACToken::burn function is expected to apply a burn tax based on the user’s desired amount multiplied by the burnTaxRate. However, the way _update processes the tax distribution causes the feeCollector to receive less than the expected burn tax amount.
Consider the following scenario:
A user holds 100e18 RAAC tokens and intends to burn them.
The burnTaxRate is set to 0.5% (default value).
According to expectations, the feeCollector should receive 100e18 * 0.5% = 5e17 RAAC tokens
The RAACToken::burn function calls _transfer(msg.sender, feeCollector, taxAmount). This invokes _update(from, to, amount) with (user, feeCollector, taxAmount).
Inside _update, the if-condition fails to check to == feeCollector, leading to an additional tax application:
This results in:
Additional amount * burnTaxRate being burned to the zero address.
Only the remaining amount being sent to feeCollector, reducing its expected fee.
Below is a test case demonstrating the issue:
Problematic code section: https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/RAACToken.sol#L185
FeeCollector Receives Less Fees: The incorrect tax accounting results in the feeCollector receiving fewer tokens than expected.
Incorrect Burn Mechanism: The function unintentionally redistributes the burn tax incorrectly, deviating from expected behavior.
Manual Review
To fix this issue, update _update to correctly check when to == feeCollector and prevent double taxation:
By implementing this fix, the feeCollector will receive the expected burn tax, ensuring proper accounting and preventing unintended losses.
This is by design, sponsor's words: Yes, burnt amount, done by whitelisted contract or not always occur the tax. The feeCollector is intended to always be whitelisted and the address(0) is included in the _transfer as a bypass of the tax amount, so upon burn->_burn->_update it would have not applied (and would also do another burn...). For this reason, to always apply such tax, the burn function include the calculation (the 2 lines that applies) and a direct transfer to feeCollector a little bit later. This is done purposefully
This is by design, sponsor's words: Yes, burnt amount, done by whitelisted contract or not always occur the tax. The feeCollector is intended to always be whitelisted and the address(0) is included in the _transfer as a bypass of the tax amount, so upon burn->_burn->_update it would have not applied (and would also do another burn...). For this reason, to always apply such tax, the burn function include the calculation (the 2 lines that applies) and a direct transfer to feeCollector a little bit later. This is done purposefully
This is by design, sponsor's words: Yes, burnt amount, done by whitelisted contract or not always occur the tax. The feeCollector is intended to always be whitelisted and the address(0) is included in the _transfer as a bypass of the tax amount, so upon burn->_burn->_update it would have not applied (and would also do another burn...). For this reason, to always apply such tax, the burn function include the calculation (the 2 lines that applies) and a direct transfer to feeCollector a little bit later. This is done purposefully
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.