Link to Affected Code:
Description:
The burn
function uses _transfer
to send the taxAmount
to feeCollector
, which triggers the overridden _update
function in RAACToken. This reapplies the swap and burn taxes on the taxAmount
, leading to double taxation on the same tokens.
Impact:
Fee collector receives fewer tokens: A portion of the taxAmount
is diverted to the burn address due to secondary taxation.
Proof of Concept:
Initial Setup:
swapTaxRate = 100
(1%), burnTaxRate = 50
(0.5%).
User calls burn(1000)
, generating taxAmount = 5
(0.5% of 1000).
Tax Transfer:
_transfer(msg.sender, feeCollector, 5)
triggers _update
.
_update
applies a 1.5% tax on the 5
tokens:
Swap tax: 5 * 1% = 0.05
→ Sent to feeCollector
.
Burn tax: 5 * 0.5% = 0.025
→ Burned.
Remaining: 5 - 0.075 = 4.925
→ Sent to feeCollector
.
Result:
User’s Loss: 5
tokens (correct).
Fee Collector Receives: 0.05 + 4.925 = 4.975
(instead of 5
).
Burned: 0.025
(not part of the original burn).
Recommended Mitigation:
Use super._update
to bypass the tax logic during tax transfers:
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.