In the burn function of RAACToken, when collecting the burn tax fee, the implementation incorrectly uses _transfer instead of directly updating balances, causing the tax fee itself to be taxed again due to the overridden _update function.
The issue lies in how the burn function handles the tax collection. Let's break down the flow:
First, let's look at the burn function:
The key problem is in using _transfer for tax collection. The _transfer function ultimately calls the overridden _update function, which applies taxes again to the tax amount being transferred and keep in mind that when taxing the amount again, there is a a burn tax, meaning that always a small portion will be lost forever instead of going to feeCollector.
Looking at the _update function:
When transferring the tax amount to the fee collector using _transfer, this _update function gets called again, applying another layer of taxes to what was already a tax payment. This creates an unintended double taxation scenario.
Notice that only swap tax part of the tax goes back to the fee collect but the remaining portion is unneccesarily being burnt. i.e super._update(from, address(0), burnAmount);
User wants to burn 1000 tokens
With burnTaxRate of 50 (0.5%), initial tax = 5 tokens
When transferring these 5 tokens to feeCollector:
The transfer triggers _update
These 5 tokens get taxed again (with both burn and swap tax)
Results in less than 5 tokens actually reaching the feeCollector
The fee collector receives less than the intended tax amount (since a portion of the actual tax will also be burned)
Manual code review
Modify the burn function to use super._update directly instead of _transfer:
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.