```\nAttackerContract.flashLoan() ->\n    // Borrow lots of tokens and trigger a callback.\n    SomeProtocol.flashLoan() ->\n        AttackerContract.exploit()\n\nAttackerContract.exploit() ->\n    // Join a Balancer Pool using the borrowed tokens and send some ETH along with the call.\n    BalancerVault.joinPool() ->\n        // The Vault will return the excess ETH to the sender, which will reenter this contract.\n        // At this point in the execution, the BPT supply has been updated but the token balances have not.\n        AttackerContract.receive()\n\nAttackerContract.receive() ->\n    // Liquidate a position using the same Balancer Pool as collateral.\n    BlueBerryBank.liquidate() ->\n        // Call to the oracle to check the price.\n        BalancerPairOracle.getPrice() ->\n            // Query the token balances. At this point in the execution, these have not been updated (see above).\n            // So, the balances are still the same as before the start of the large pool join.\n            BalancerVaul.getPoolTokens()\n\n            // Query the BPT supply. At this point in the execution, the supply has already been updated (see above).\n            // So, it includes the latest large pool join, and as such the BPT supply has grown by a large amount.\n            BalancerPool.getTotalSupply()\n\n            // Now the price is computed using both balances and supply, and the result is much smaller than it should be.\n            price = f(balances) / pool.totalSupply()\n\n        // The position is liquidated under false pretenses.\n```\n