Back to Blog

The Analysis of the Popsicle Finance Security Incident

Code Auditing
August 4, 2021

On Aug 4th, 2021, Popsicle Finance suffered a huge financial loss (over $20M) from an attack [1]. After manual analysis, we confirm that it is a double-claiming attack, i.e., a loophole of its reward system allows the attacker to claim rewards repeatedly. In the following, we will use an attack transaction to illustrate the attack process and the root cause of the vulnerability.

Background

Popsicle Finance is a yield optimization platform which supports multiple vaults for different chains (e.g., Ethereum and BSC).

Specifically, a user first invokes the deposit function to provide liquidity, and gets Popsicle LP token (PLP for short). After that, Popsicle Finance will manage the liquidity (interacting with platforms like Uniswap) for the user to make profits. The user can invoke the withdraw function to fetch back the liquidity from Popsicle Finance, which will calculate the amount based on the PLP tokens. The incentive reward comes from the liquidity, which will be accumulated as the time goes by. The user can invoke the collectFees function to claim the rewards, which is the key of this attack.

Vulnerability Analysis

In the collectFees function, token0Reward and token1Reward (rewards of the corresponding LP token pair) are calculated for the user. The whole calculation logic is straightforward. However, the function uses a modifier named updateVault, which is used to update the rewards accordingly.

In short, updateVault will:

  1. first invokes the _earnFees function to get accumated fee from the pool;
  2. then invokes the _tokenPerShare function to update token0PerShareStored and token1PerShareStored, which represent the amounts of token0 and token1 in the pool for each share;
  3. finally invokes _fee0Earned and _fee1Earned functions to update the rewards for the user (i.e., token0Rewards and token1Rewards respectively).

Functions _fee0Earned and _fee1Earned share the same logic, i.e., implementing the following fomula (use token0 as an example):

user.token0Rewards += PLP.balanceOf(account) * (fee0PerShare - user.token0PerSharePaid) / 1e18

Note that the calculation is incremental, which means even the user does NOT hold PLP token, the calculated reward remains the value stored in token0Rewards.

Hence, we can conclude the following two observations:

  1. user's rewards are stored in token0Rewards and token1Rewards, which are not associated with any PLP token;
  2. the collectFees function only relies on the status of token0Rewards and token1Rewards, which means that rewards can be withdrawn without holding PLP token.

In the real world scenario, it means a user deposits money to a bank and the bank gives her a certificate of the deposit. Unfortunately, this certificate is neither anti-counterfeiting, nor associated with the user. In such a case, it is possible to make duplicates and spread them to others to gain profits from the bank.

Attack Flow

Briefly, the attacker took the following steps to launch the attack:

  1. created three contracts. One of them was used to launch the attack, while other two were used to invoke the collectFees function to fetch the rewards;
  2. utilized the Flash Loan, i.e., borrowing a large amount of liquidity from AAVE;
  3. launched the Deposit-Withdraw-CollectFees cycle to perform the attack (there are 8 cycles in total, and lots of liquidity has been withdrawn from multiple valuts of Popsicle Finance);
  4. returned the Flash Loan back to AAVE, and laundered the profits through Tornado.Cash.

Specifically, the Deposit-Withdraw-CollectFees cycle consists of several steps, which can be easily labelled and clearly summarized by using our online tool [2]:

Profit Analysis

In total, the attacker harvested $20M from Popsicle Finance, including 2.56K WETH, 96.2 WBTC, 160K DAI, 5.39M USDC, 4.98M USDT, 10.5K UNI. After that explotiation, the attacker first exchanged all the other tokens to ETH through Uniswap and WETH, and then performed money laundering by using Tornado.Cash.

Credits

Yufeng Hu, Ziling Lin, Junjie Fei, Lei Wu, Yajin Zhou @BlockSec

(In alphabetical order by the last name)

https://www.blocksecteam.com

Medium: https://blocksecteam.medium.com/

Twitter: https://twitter.com/BlockSecTeam

Contact: [email protected]

Reference

[1] https://twitter.com/defiprime/status/1422708265423556611

[2] https://tx.blocksecteam.com/

Sign up for the latest updates
Bitget's $387.5M Off-Chain Breach: Beyond Keys and Contracts
Security Insights

Bitget's $387.5M Off-Chain Breach: Beyond Keys and Contracts

On September 24, 2026, attackers exploited a vulnerability in a third-party security product, obtained internal credentials, and forged withdrawal commands. The resulting transfers moved approximately $387.5M from some of Bitget's operational wallets across Ethereum, other EVM networks, XRP Ledger, Zcash, and TRON; private keys and cold wallets remained intact. This deep dive summarizes the disclosed incident path and fund flow, examines rapid conversion into native assets and the ecosystem recovery response, proposes a systematic defense-in-depth framework for institutions, and explains how authorized blockchain penetration testing can validate cross-layer assumptions.

~$11.3M Lost: Multicall Router, Nostra | BlockSec Weekly
Security Insights

~$11.3M Lost: Multicall Router, Nostra | BlockSec Weekly

This report, covering 2026/09/14 - 2026/09/20, examines two security incidents with approximately $11.3M in combined losses, on Ethereum and Starknet. In the larger one, a multicall router accepted its own address as a dispatch target, so the nested call reached the Gateway module of a Safe wallet carrying the router's own already-authorized identity instead of the external caller's, and roughly 2,900 `aEthrsETH` was routed out of that wallet into an attacker-created Uniswap v4 pool. On Starknet, Nostra's oracle integration required a minimum of only one aggregated source, so when only two of the three configured price sources reached the aggregation, a manipulated thin-pool quote averaged with a normal quote to value `NSTR` at roughly $49.52, supporting approximately $3.5M of borrowing against overvalued collateral.

~$320M Lost: Liquid Network, Symbiosis Exploits | BlockSec
Security Insights

~$320M Lost: Liquid Network, Symbiosis Exploits | BlockSec

This report, covering 2026/09/07 - 2026/09/13, examines two security incidents that caused approximately $320M in losses, including the Liquid Network exploit of 2026/09/06 that the previous report did not cover. The larger was that Liquid Network exploit, where the rangeproof validation cache in Elements derived its key by hashing four fields — two of them variable in length — concatenated with nothing marking the boundaries between them, so a verdict recorded for one output was returned for another whose proof was never examined, letting the attacker create 4,000 unbacked L-BTC and peg out nearly all of them as bitcoin. On the Bitcoin route of the Symbiosis cross-chain bridge, spanning BNB Smart Chain, Ethereum and Rootstock, off-chain code that reads Bitcoin deposits took the depositor's identity from a field the depositor controls and then subtracted its fee from the deposit without checking whether the fee itself was negative, letting a 330-satoshi deposit mint `46,116,860,184.27388234 syBTC`; the pools it had to be sold through held only 11.26 syBTC, so the loss to liquidity providers and users came to an estimated 9.97 BTC (~$770K).

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit