During the RAACToken's burn function, the fee collector receives less tokens than intended due to an unintended double taxation mechanism. While the user burns the correct amount, the fee portion meant for the fee collector is incorrectly taxed again during transfer, resulting in a portion of the fees being burned instead of distributed.
The issue occurs in the interaction between the burn() and _update() functions:
When a user burns tokens, the following sequence occurs:
Correct tax amount is calculated based on burnTaxRate
The main amount is properly burned
_transfer() gets triggered with the taxAmount
The _transfer() triggers _update which applies additional taxes to the fee portion
Example Scenario
For a burn of 1000 tokens with:
burnTaxRate = 50 (0.5%)
swapTaxRate = 100 (1%)
Combined tax rate = 150 (1.5%)
Current behavior:
User initiates burn of 1000 tokens
Initial burn tax calculation: 1000 * 0.5 / 100 = 5 tokens
995 tokens are burned from user
The 5 token fee transfer triggers _update:
Additional 1.5% tax on 5 tokens ≈ 0.075 tokens
Fee collector receives ~4.975 tokens instead of 5
~0.025 tokens are unexpectedly burned
Expected behavior:
User burns 1000 tokens
995 tokens are burned
Fee collector receives exactly 5 tokens
No additional taxes on the fee portion
In order to run the test you need to:
Run foundryup to get the latest version of Foundry
Install hardhat-foundry: npm install --save-dev @nomicfoundation/hardhat-foundry
Import it in your Hardhat config: require("@nomicfoundation/hardhat-foundry");
Make sure you've set the BASE_RPC_URL in the .env file or comment out the forking option in the hardhat config.
Run npx hardhat init-foundry
There is one file in the test folder that will throw an error during compilation so rename the file in test/unit/libraries/ReserveLibraryMock.sol to => ReserveLibraryMock.sol_broken so it doesn't get compiled anymore (we don't need it anyways).
Create a new folder test/foundry
Paste the below code into a new test file i.e.: FoundryTest.t.sol
Run the test: forge test --mc FoundryTest -vvvv
Fee collector receives less than the intended fee amount
Part of the fees are unintentionally burned
Inconsistency between expected and actual fee collection
Foundry
Manual Review
Exclude the feeCollector in the _update function when it's the receiver address (additional exclude it if it is the sender address if you don't want the feeCollector to pay fees during 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.