Postmortem Report on the details of the events of May 14, 2024

Predy Finance
4 min readMay 16, 2024

--

Predy Finance experienced an unintended attack on May-14–2024 07:41:28 AM +UTC.

As a result, the assets in Predy Pool were entirely drained.
Below are the details of the attack analyzed by us.

Attack steps by the attacker

Step 1. Creation of Contract

The attacker deployed a contract for the attack.
This was deployed on Mar-29–2024. All subsequent steps were executed from this Attacker Contract.

Step 2. Creating a pair

Predy Pool manages the liquidity of currencies.
This is used to apply leverage when creating positions in Predy.
Anyone can add new currency pairs.
Before the attack, Predy had the following two pairs:

pair id 1 : pair of Bridged USDC and Wrapped ETH
pair id 2 : pair of Native USDC and Wrapped ETH
The Attacker Contract created a new pair, pair id 3.
This is a pair of Native USDC and Wrapped ETH, similar to pair id 2.

pair id 3: pair of Native USDC and Wrapped ETH

In Predy, creating the same pair is not an issue by design. However, the pair creator can restrict the contracts that trade with the pair. Therefore, the Attacker Contract does not have the authority to trade with existing pairs. Below is the line that restricts trading authority.

Thus, the Attacker Contract created a new pair.
At this stage, the Attacker Contract only created the new pair and did not provide funds.

Order of function calls

  1. PredyPool.registerPair
  2. AddPairLogic.addPair

Step 3. Execute the trade function

In the newly created pair id 3, the Attacker Contract has the authority, so the trade function can be executed. The Attacker Contract calls the trade function specifying pair id 3 of Predy Pool. Within this, `TradeLogic.trade` is called, and then `TradeLogic.callTradeAfterCallback` is called. And within this, `AttackerContract.predyTradeAfterCallback` is called.

This was implemented to call the predyTradeAfterCallback of Predy’s MarketContract. A mutex is used for the trade function call, so the trade function cannot be called again within the predyTradeAfterCallback function.

However, here the take function can be called to obtain liquidity from Predy Pool or the supply function to provide liquidity. This is necessary to utilize liquidity within the trade function.

Order of function calls

  1. PredyPool.trade
  2. TradeLogic.trade
  3. TradeLogic.callTradeAfterCallback
  4. Attacker Contract.predyTradeAfterCallback

Step 4. Execute take and supply within the predyTradeAfterCallback function

Within the predyTradeAfterCallback of the Attacker Contract, When the `PredyPool.take` function is called specifying the amount of USDC in Predy Pool, this amount is transferred to the Attacker Contract. This amount is the same as the amount existing in pair id 2. The Attacker Contract then calls `PredyPool.supply`, supplying the amount of USDC obtained by the take function to pair id 3. The same was done for Wrapped ETH.

Finally, a difference check is performed before and after the trade process within the Pool.

Since the assets in the Pool simply moved from pair id 2 to pair id 3, there is no difference in the amounts within the pool. Therefore, the trade function call from the Attacker Contract was judged as a normal transaction.

Order of function calls

  1. in callback: PredyPool.take
  2. in callback: PredyPool.supply
  3. in TradeLogic.callTradeAfterCallback: globalData.finalizeLock

Step 5. Execute the withdraw function

The state where the Attacker Contract has supplied funds to pair id 3 remains in Predy Pool. The Attacker Contract executed the withdraw function and obtained funds from the created pair.

The funds withdrawn in this manner are 219,585.737814 USDC and 83.9 WETH.

Conclusion

The above details the fund outflow in this incident.
This issue was caused by the unintended use of a callback, which we could not discover before deployment. We have already stopped the Contract, so there will be no further damage. Countermeasures are already under consideration, and we will ensure a secure state to provide it to everyone safely. Resuming operations are considered at an appropriate time after undergoing an audit. updates will be provided as soon as details are finalized.

--

--

Predy Finance

Most capital efficient Perpetual Options based with Uniswap. Live on Arbitrum.