The RAACToken::burn() function applies the burn tax rate twice - once explicitly in the burn function and once implicitly through the RAACToken::_transfer() call to the fee collector. This results in the fee collector receiving less than the intended tax amount and more being effectively burned.
The RAACToken::burn() function first calculates and applies the burn tax:
However, the RAACToken::_transfer() call to send the tax to the fee collector internally calls RAACToken::_update(), which applies the tax rates again:
When transferring the burn tax to the fee collector, the transfer itself is taxed again, causing the fee collector to receive less than intended.
The likelihood is HIGH because it will happen on every burn operation.
The impact is MEDIUM because while it consistently affects protocol revenue, it does not pose a direct risk to user funds or core protocol functionality.
Manual review
Use super._update() instead of _transfer() in the burn() function to avoid the double tax calculation:
This ensures that tax is only applied once during burns, maintaining the intended tax rate.
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.