Audit Anomalies Archive — Issue#11
Ihave noticed that crypto projects often attempt to re-launch or stay relevant by migrating to a new token. This can happen for various reasons, such as issues with the tokenomics of current token, reviving the project etc., I would like to share one interesting finding from auditing one such project involving token migration.
Swapping
Let’s take a simplified example from the actual contract, using the CoinSwitch
contract as an illustration. Currently, it holds new tokens that need to be distributed to users. The distribution event spans 90 days, after which oldTokens
cannot be redeemed for new tokens. The mapping is a straightforward 1:1 ratio. Users are required to call the swapTokens
function and specify a lock period for the redeemed new token.
function swapTokens(uint256 lockPeriod) external {
require(block.number <= eventEndBlock, "eventEndBlock reached");
_swapTokens(lockPeriod, oldToken.balanceOf(msg.sender));
}
Additionally, there’s a helper function called burnOldTokens
to permanently remove all oldTokens
out of supply, which can only be executed after the event is concluded. These are tokens of the users who swapped the old tokens for the new once.
function burnOldTokens() external onlyOwner {
require(block.number > eventEndBlock, "eventEndBlock yet to finish");
uint256 balanceOldToken = oldToken.balanceOf(address(this));
oldToken.transfer(address(0), balanceOldToken);
}
So, what’s the issue?
On the surface, it may seem straightforward — users swap old tokens for new tokens. However, the potential problem doesn’t arise from the contract itself but from user behavior. “Consider a scenario where only 75% of users decide to swap their tokens, while the remaining 25% choose not to do so for some reason.” This results in 25% of the new tokens being stuck in the contract. Unlike burnOldTokens
, there is no function to handle the unswapped new tokens. Consequently, after the 90 days, the new tokens that were never swapped remain trapped in the contract indefinitely.
The Learning
In short, the above attack vector is heavily reliant on actual users of the protocol. Therefore, it’s essential to consider the perspective of a typical user. There are three distinct perspectives here. Firstly, the developer may assume that all users will swap their tokens. Secondly, from a hacker’s viewpoint, they may attempt to break the contract and steal the new tokens. Or, they might try to swap new tokens without providing any old tokens. But when considering the protocol from the perspective of a normal user, you might uncover this finding that users have a choice whether to perform an action or not.
For a code sample to understand the issue, pseduo source code can be found in this link.
Thanks for Reading !