Back to all stories# TL:DR

# Attacker Addresses & Exploit Transactions

## Attacker account:

## Attacker contract addresses:

## Exploit transactions:

## Exploited contracts:

# Attack Flow

# Code Vulnerability

# Profit and Assets Tracing

# Can the issue be found during the audit?

# Reference

Whale Loans Incident Analysis

6/20/2022

**On 20 June, Whale Finance experienced two separate exploits on the project's stablecoin AMM contracts, which has led to ~$12k in losses. The attacker called the swap() function from the USDT/BUSD pool which had a vulnerability, which was primarily caused by an incorrect k invariant calculation when the swap pair is a stablecoin.**

**On the projects Discord, @coj337 who is the servers admin stated that due to the exploit, Whale.Finance has taken down the DEX whilst the vulnerability is being assessed. It has also been announced that no customer funds were lost due to the exploit.**

- For the first attack on Stable AMM - USDT/BUSD: 0xf9553
- For the second attack on Stable AMM - USDC/BUSD: 0xaa85f

We take the first exploit transaction on Stable AMM - USDT/BUSD as an example.

- The attacker called the function
**swap()**from the Stable AMM -USDT/BUSD contract. The input amount of USDT(BSC-USD) is 5964, which is the balance of the USDT in the Stable AMM -USDT/BUSD contract. - The attacker sent back 0.6 USDT to the Stable AMM -USDT/BUSD contact.
- After the fee adjustment, the balance0Adjusted =
**6022457770012534500304,**and the balance1Adjusted =**59471946427871433983220000,**which means the k invariant value is**1266806331900666880877818210684878792429048115.** - However, the reserve0 and reserve1 are
**59646190399283805000316**and**5947194642787143398322**respectively. Their corresponding k invariant value is**2516642811824473716920890881639825**. If it is multiplied by 10000**2 = 100_000_000, the result is still less than the k invariant given by the balance0Adjusted and balance1Adjusted. In this case, the k invariant validation was bypassed. Therefore, the Stable AMM - USDT/BUSD contract transferred the 5964 USDT to the attacker successfully.

5. Similarly, the attacker called the function **swap()** twice to swap the BUSD with input amount 5947 and 62 of BUSD, respectively.

- Due to the same vulnerability, the Stable AMM -USDT/BUSD contract transferred 5947 and 62 BUSD to the attacker directly.
- The attacker applied the same strategy to the Stable AMM-USDC/BUSD with 232 USDC and 232 BUSD as profit.
- In total, the attacker made a profit of $12K.

The root cause of the vulnerability is the incorrect k invariant calculation when the swap pair is stable coin. In contract **WhaleswapPair.sol**, the calculation of k invariant in function _**k()** while the boolean **stable** is set to true is x ^ 3 * y + y ^ 3 * x instead of the regular one k = x * y.
In function **swap()** from the Stable AMM-USDT/BUSD contract, the following **require** statement checks the k invariant returned by the aforementioned function _**k()** while **stable** is set to true. However, in the stablecoin case, the k equals to x ^ 3 * y + y ^ 3 * x, so the k invariant check should be using 10000 ^ 4 instead of 10000 ^ 2 because the formula calculating the k invariant is of degree 4. The correct implementation of the require statement should be require(_k(balance0Adjusted, balance1Adjusted) >= _k(_reserve0, _reserve1).mul(10000**4), 'Whaleswap: K').

At the time of writing, all funds have been transferred from two attacker contracts to the attacker account 0xd793f. The attacker account currently has the exploited 5964 USDT, 6178 BUSD, and 232 USDC.

Yes, we will be able to spot this issue because the k invariants of non-stablecoin and stablecoin cases are different.

https://twitter.com/WhaleLoans https://docs.whale.loans/resources/contracts