Impossible Finance v2 Swap Jun 21st Postmortem
To our Impossible Community,
To our Impossible Community,
Our v2 swap was attacked via a flash loan and a malicious ERC20 token. Fortunately, this was an isolated incident and the exploited funds do not have a significant impact on Impossible’s path forward. All users who deposited into liquidity pools (“LPs”) prior to the attack will be 100% compensated. We are confident for a full recovery and have plans to emerge stronger from this. For regular updates on Impossible, please join our Official Telegram Announcement Channel.
We understand that users’ funds and safety are paramount, and will be working on a full compensation plan as part of our SAFU (insurance) fund program. All users that deposited funds into liquidity pools (“LPs”) for non-stablecoin pairs (such as IF/BUSD, IF/BNB) prior to the attack (at 4:40 AM UTC, 21 June 2021) will be 100% compensated for any losses incurred. We will release more details on the exact compensation plan details in the coming days. Users should always have peace of mind when depositing into Impossible Finance, and we look forward to working with additional security teams, auditors, independent reviewers, bug bounties, and more, to ensure that users’ funds will always be safe. Let’s keep building the Impossible!
For the detailed event report, below we have conducted a post-mortem analysis of this incident:
Overview of exploiter’s transactions:
This attack creates evil ERC20 tokens (AAA and BBB) and hijacks the swapExactTokensForTokens() FUNCTION call when the evil token’s own transferFrom() is invoked
Hijack occurs in the call to TransferHelper.safeTransferFrom in SwapExactTokensForTokens
This hands control to evil ERC20, which calls swap() before our SwapExactTokensForTokens calls cheapSwap(), performing two swaps without adjusting the price in between.
The user then repeats this action in order to drain the pools.
The assets in our pool that were affected were only our non-boosted, normal xyk pools. Our new xybk boosted pools were not affected by this attack, but we will make sure to add additional precautions on the xybk pools as we move forward.
Funds left BSC through the Anyswap bridge to Ethereum, and are now located here: https://etherscan.io/address/0x8e0d334a77614a7ce089c9246e9b1d7c7172ef02
This address is affiliated with a few other ethereum-based addresses and we are investigating these addresses at the moment.
The attack in a picture:
The attacker uses a custom token to call swap at the pair level after getAmountsOut performs the x*y=k check. After which, _swap in the router calls cheapSwap which does not have x*y=k checks. The uniswap invariant is supposed to quote a higher price for sequential swaps but with the custom token, the adversary was able to get 2 trades at the same price without slippage. The adversary borrowed capital with a flash loan to leverage this weakness and drain our pools.
We note that at the time of the attack, we had ~$1.5M TVL in our pools split between stablecoin pools and regular uniswap invariant pools. Because of how our custom-built xybk formula reduces trade slippage throughout the entire range of the pool, the adversary could not use the above attack on our stablecoin pools. As such, approximately only $450K in BNB and BUSD, and about $250K in IF governance token were lost through this attack.
Our code and the design decisions we made
The truth is that although our swap was attacked due to the missing x*y=k condition, leaving that line out was an intentional design decision, in conjunction with several safety measures that we took that ultimately were not sufficient. Although our codebase is forked from Uniswap V2, we added significant novel functionality on top, such as the xybk invariant for more capital efficient stablecoin swaps, as well as asymmetric tuning. These additional functionality allows our swap to compete with the state-of-the-art of AMM swaps without forking their research. We assert that these following ideas we came up with were carefully decided on after first educating ourselves on all AMM swap theory. These novel features required a huge redesign of the core Uniswap V2 contracts, which we studied for many months before embarking on this path. During our development process, we studied the codebase of Uniswap V2 and realized that this x*y=k check is already done implicitly at the router level before being performed at the pair contract level:
The math involved for a regular x*y=k swap is easy, however, our added customizations (custom fees per pool, xybk or uniswap invariant per pool) would require significantly more gas to perform this double calculation. We made the call to reduce the redundancy and increase gas efficiency by forcing cheapSwap calls to route through only our router (which performs the x*y=k check) and leaving out the check at the pair level. This would make swaps for our users cheaper by approximately 20%. This means that if a transaction were to cost $5 on ETH mainnet, this would save every user $1 per trade. Especially as we move towards the multi-chain ecosystem, we need to consider the gas footprint we leave with our swap across all chains. Based on the usage of Uniswap in the last week, this savings would have led to 471,000 transactions saving at least a dollar per trade at gas prices of ~10 wei. In the heat of defi summer with gas prices between 100–500 gwei, this feature could potentially save up to $25 million USD each week in gas fees for users.
Regarding the similarity to the BurgerSwap attack — this was something on our radar during our development and audit process. We reviewed this attack during our development process and to ensure that our swap did not fall victim to the same attack, we strengthened the security by adding OpenZeppelin reentrancy guards at the router and pair level for all external calls. On top of that, we added a custom whitelist feature that ensured that custom token attacks would not be possible. Unfortunately, this whitelist feature was not fully functioning because we did not have a parameter to update this whitelist function, which was a crucial function that our audits, peer reviews, and ourselves missed, otherwise we would’ve chosen to turn it on, but instead this avenue of attack slipped through our scrutinization. If we had the whitelist feature turned on or if “onlyIFRouter” modifiers were also on the swap function （at the tradeoff of higher gas costs), our swap would be secured against these attacks. We deeply regret that this happened and inconvenienced our users and liquidity providers. In the future, the whitelist feature will be a core structure utilized for various products to ensure a family of similar attacks, such as evil Pickle jars, are avoided.
We spent the last 24 hours reviewing our internal development process and added internal safeguards to ensure that vulnerabilities such as the above would be caught during the development phase.
We are planning to fix this vulnerability and redeploy our swap. Any of the following actions would be individually sufficient to prevent such an attack again:
Adding a whitelist feature for tokens
Adding an onlyIFRouter modifier to the swap function at the pair level
Adding the x*y=k check at the pair level
Adding a global pause on trading in case any suspected attack vectors are identified preemptively
However, we plan to implement all 4 fixes — our priority moving forward is to 110% safeguard the funds of the IF community, and do not want to rely on just one solution to make things better moving forward.
Thanks to PeckShield for being the first team to find the attack, and Tascha from Alpha Homora for putting us in touch. https://twitter.com/peckshield/status/1406839879305101313
For additional comments on the exact sequence of transactions, please check out @Watchpug https://watchpug.medium.com/impossible-finance-exploit-root-cause-analysis-ba0ed7c151e4. We appreciate both teams’ support, as well as many other community members, investors and partners who reached out to support us during this incident.