Back to all stories
Incident Analysis
Pike Finance Incident Analysis
Pike Finance Incident Analysis

Incident Summary

On 26 April Pike Finance was exploited for $299k. The project announced via their X account that the root cause was due to a forged CCTP message which allowed the attacker to drain funds. As a result of the exploit they paused their contract which required it to be upgraded first.

Screenshot 2024-05-23 at 14.12.25

The upgrade caused a knock on effect and on 30 April 2024 Pike Finance was exploited for a second time with approximate losses of $1.68m. Due to the way in which delegatecall works with upgradeable contracts, the addition of the pause() and unpause() function caused a collision in storage. The contract was then able to be initialized for a second time by the attacker, giving them elevated access.

Exploit Transactions

Incident 1

Incident 2

Attack Flow

The Build Up

26 April: Pike Finance was exploited for ~$299k. The project paused the contract while they investigated and addressed the vulnerability. As the project didn't contain a pause function it needed to be upgraded first.


When comparing the proxy contracts initial implementation (0x634683d7079af2EBeC84637BBC29dbD6FE817564) against the new upgrade (0xD167A1893e8F108572826dAbAe19663A9131b0c2), we can see the newly added Unpaused() and Pause() functions.

Screenshot 2024-05-23 at 14.12.56

The Second Exploit

30 April: Though the contract had remained paused, Pike Finance was exploited for ~$1.68m.

The attack flow is based on the following transaction.

  1. The attacker called the initialize() function from proxy contract. Under normal circumstances this function is not callable more than once. The initialize() function set the isActive variable to the attacker’s address.


  1. With the isActive role, the attacker could call the upgradeToAndCall() function and upgrade the proxy contract to their own malicious implementation.


  1. The malicious upgrade allowed the attacker to withdraw 479.39 ETH from the the vulnerable contract.


  1. The attacker repeated the same process on Arbitrum and Optimism chains.


The vulnerability is due to complexities with using delegateCall and upgradeable proxy contracts. Delegatecall allows a contract to load code from a different address at runtime.

"Storage, current address and balance still refer to the calling contract, only the code is taken from the called address."

Any change to the state by the called contract affects the calling contract state. If the state of the called contract isn’t identical to the calling contract, it can lead to incorrect behaviour. In the case of Pike Finance the state variable showing the contract was initialized returned false instead of true.

The Paused() and Unpaused() functions that were added in an upgrade after the initial incident return a boolean flag (true of false) which only occupies one bit of storage. This means they can fit and be included in a storage slot that isn’t using up its allocated space, creating a difference between the two contracts. This difference then causes the collision. In Pike Finance, the upgrade assigned 0 bytes to the initialization flag, erasing its state. Below is the state change from txn 0xf3f where the newly added unpause() function was called.


The attacker could therefore call initialize() again as the contract believed it had never been initialized.


Money Flow

Summary of Losses

(Approximate at time of exploit)

Incident 1

Arbitrum: $299,107

Incident 2

Arbitrum: $100,070.00

Ethereum: $1,433,977.23

Optimism: $150,458.95

Total: $1,983,633.18

The attack wallet from the first incident withdrew 1 BNB withdrawal from Tornado Cash on BSC before bridging a small amount to Arbitrum via Orbiter bridge to cover fees. After the exploit $299k was bridged from Arbitrum to Ethereum via Stargate bridge and deposited into Tornado Cash from the attacker’s wallet, 0xAdaF1626aEC26A7937aE7d1Fa0664e6E0904C1d0. After the Tornado deposits a small amount of left over ETH 0.043, too small to deposit to Tornado Cash, was sent to a Binance account.

In the second incident the attacker funded their Arbitrum wallet via Railgun which then bridged to Optimism. The exploited funds from both Arb and OP were then bridged to Ethereum via Li.Fi bridge before the exploit transaction on Ethereum was executed. 562 ETH was then transferred back to a Railgun wallet, at which point the funds are shielded, hiding their trail.


This incident is a rare case of a reaction to one exploit leading to the cause of a second exploit and highlights the cutthroat nature of web3. The issue exploited in the second incident is a widely known one but easy to overlook, especially when dealing with the pressure of having been exploited for $299k. The $1.68 million loss from the second exploit was the 5th largest incident we recorded in April.

To find out more about CertiK’s audit process and how we can help protect your smart contracts visit or contact us via the social media listed on