SMART CONTRACTS, DEFI & WEB3 SECURITY - Chapter 3, Exercise 2 Solution ========================================================== Why Pushing Payment to 50 Addresses in a Loop Is Risky PROBLEM ------- A contract needs to pay out prize money to 50 different competition winners at once. Using this chapter's own pull-over-push reasoning, explain what could go wrong with a loop that sends ETH directly to all 50 addresses in a single transaction, and how the pull pattern avoids it. SOLUTION -------- THE PUSH VERSION'S REAL PROBLEM If the contract loops through all 50 winner addresses and sends ETH directly to each one inside a single transaction, every single one of those 50 sends is an INTERACTION - an external call to another address, exactly the kind of operation this chapter's own Checks-Effects-Interactions material treats as risky. The specific danger: if even one of those 50 addresses belongs to a contract that's broken, deliberately malicious, or simply doesn't accept plain ETH transfers (some contracts reject them outright, or run out of gas trying to process one), that single failed send can cause the entire loop - and therefore the entire transaction - to revert. Because Solidity transactions are all-or-nothing by default, one bad recipient out of 50 can block payment to all 49 legitimate winners as well, even though nothing was wrong with their own addresses at all. Worse, a malicious recipient could deliberately craft their own contract to always fail on receiving ETH specifically to grief everyone else in the same payout - or, using the reentrancy mechanism this chapter already covered, to try draining more than their own fair share instead. HOW PULL OVER PUSH AVOIDS THIS Instead of actively sending ETH to all 50 addresses in one shared transaction, the contract would instead record how much each of the 50 winners is separately owed (as internal accounting, similar to the balances mapping in this chapter's own withdraw() example), and let each winner call their own separate withdraw() transaction whenever they choose to claim their prize. This fully isolates the 50 payouts from each other. If one winner's own address has a broken or hostile contract behind it, only that one winner's own withdrawal attempt fails - it has zero effect on whether the other 49 winners can successfully claim their own prizes, since each pull is its own fully independent transaction rather than one shared, all-or-nothing loop. ANSWER: A push-based loop risks one single bad recipient reverting the entire transaction and blocking payment to everyone else, since all 50 sends are bundled into one all-or-nothing operation. The pull pattern avoids this by having each winner independently withdraw their own recorded amount in a separate transaction, so one broken or malicious recipient can only ever affect their own payout, never anyone else's. ---- WHY THIS WORKS AS AN ANSWER This identifies the specific real failure mode (one bad recipient reverting a shared, all-or-nothing loop) and explains precisely how isolating each payout into its own independent transaction removes that shared point of failure entirely, rather than just restating "pull is safer than push" without the underlying mechanism.