Recent research by the Beosin security team found that security incidents caused by deflationary tokens are still frequent, resulting in the loss of funds for many projects. Therefore, we wrote this article on deflationary tokens to share with you.
1 What are Deflationary Tokens?
Deflationary tokens are tokens that are burned in proportion to the transaction process, which is a good way to incentivize users to hold tokens.
During the token trading process, some tokens will be deducted for fees, rewards and destruction, and as the tokens are burned, the total supply will keep decreasing, which will increase the proportion of tokens held by users, then users will be more willing to hold tokens to get higher returns.
It seems to be a perfect financial solution, but there is a burning process in the code implementation, which will bypass the swap process and modify the address balance directly. Some unexpected issues may occur when this situation is combined with pair.
2 What are the Issues Exist in Deflationary Tokens?
(1) Add liquidity
The deflationary token will charge a certain percentage of fee to the current contract when transferring, and the pair contract will be called to swap, addLiquidity or sync when the fee reaches a certain threshold (the current number of tokens is greater than or equal to a variable set by the contract).
If during the deflationary token transaction, the to address is not excluded from being equal to the pair contract address, and the deflationary token is TokenB in the pair, then the operation of adding liquidity to TokenA and TokenB may result in failure.
Why does the transaction fail? Adding liquidity is done by depositing both TokenA and TokenB tokens into the pair contract, and then calling the mint function of the pair contract, which determines how many tokens the user has passed in based on the difference between the current balance and the reserve of this contract.
After the user sends TokenA tokens to pair and makes a TokenB transfer, if the fee charged happens to reach the above threshold, the token contract calls the swap, mint or sync function of pair, which will all call the _update function of pair, thus updating the TokenA that the user initially sent to pair to reserve.
Finally, the user calls the mint function, which will cause TokenA's balance and reserve to be equal, and as a result will cause the transaction to fail.
The call process:
(2) Skim function
The Pair contract has a skim function that sends tokens in the pair contract that are in excess of the reserve to the address specified by the caller, and the number is calculated based on the difference between the number of tokens in the pair contract and the reserve. This in itself is a function of balancing the pair supply, but problems can arise when one of the tokens is a deflationary token.
Deflationary tokens will deduct a certain fee in the transaction, so what happens if the fees deducted during the token transfer are paid by _from address in the skim function?
At this point, the fee deducted will be the supply of the pair, so that the tokens can be transferred to the pair in advance, through the continuous skim function and sync function to consume the supply of the pair, the price of the tokens in the pair continues to soar, and eventually a small number of the deflationary tokens can be exchanged for a large number of another token (usually usdt, eth).
The whole process:
This problem is mainly found in deflationary tokens that use a "reflection" mechanism, where there are two token balance storage variables, tOwned and rOwned, and tOwned stores the actual number of tokens, while rOwned stores the value after being inflated by the currentRate variable after reflection.
What is the role of rOwned? As mentioned at the beginning of the article, deflationary tokens provide an incentive for users to hold tokens, and the way this incentive is used is to deduct the rOwned value from the trader, and at the same time deduct rTotal, so that the ratio of rOwned to rTotal for other users will be passively increased to achieve passive gains. (rOwned and rTotal can be interpreted as the user's shares and total shares)
The user can query the balance in two ways, one is the excluded address, which directly returns the value of tOwned, and the other is the non-excluded address, which returns rOwned/currentRate, and currentRate is calculated as rTotal/tTotal. If there is a way to make rTotal decrease, the actual balance queried by the user will become larger. If the pair query balance becomes larger, then the excess tokens can be transferred out through the skim function.
There exists a deliver() function for this class of deflationary tokens, which can be called by non-excluded addresses. The function will burn the rOwned of the caller and burn the same number of _rTotal, making the balance query increase for all non-excluded addresses. If pair is non-excluded, then the arbitrage attack can be used in the above manner.
3 Related Security Incidents
(2) Crypto1319, The Philosophers Stone ($TPOS) and Meta Speed Game ($MTSG)