Pull Payment Explained in Detail
Pull payment records an owed balance and lets the recipient withdraw it in a separate call. The main flow does not depend on sending funds to an untrusted recipient immediately.
This pattern is common for refunds, auctions, rewards, and settlement systems.
{
"preset": "comparison",
"autoplaySecs": 5.0,
"edges": [
{"idx": 0, "stepReveal": 1},
{"idx": 1, "stepReveal": 2},
{"idx": 2, "stepReveal": 1},
{"idx": 3, "stepReveal": 2}
],
"steps": [
{"title": "Step 1 - Main flow chooses when to send", "desc": "Push payments send during the main action; pull payments only record owed balances.", "activeNodes": ["push-action", "pull-action"], "activeEdges": [], "tone": "info", "navLabel": "Step 1 - Main flow chooses when to send"},
{"title": "Step 2 - External calls move to the edge", "desc": "Pull payments move recipient interaction into a separate withdraw call.", "activeNodes": ["push-call", "pull-withdraw"], "activeEdges": [0, 2], "tone": "warning", "navLabel": "Step 2 - External calls move to the edge"},
{"title": "Step 3 - Failure scope changes", "desc": "A push payout can block a whole batch; a pull withdrawal should only fail for that recipient.", "activeNodes": ["push-block", "pull-isolate"], "activeEdges": [1, 3], "tone": "success", "navLabel": "Step 3 - Failure scope changes"},
{"title": "Audit lesson", "desc": "Pull payment reduces global payout DoS, but the withdrawal function still needs safe state ordering and reentrancy protection.", "activeNodes": ["pull-withdraw", "pull-isolate"], "activeEdges": [3], "tone": "success", "navLabel": "Audit lesson"}
]
}
Push payout risk vs pull payout isolation
Compare the risky flow that sends during the main action with the safer flow that records debt and lets each recipient withdraw.
Smart contract example
pendingWithdrawals[user] += amount;
The user later calls a withdrawal function.
Pull Payment in Auditing
Pull payment reduces the chance that one reverting or malicious recipient blocks the whole protocol. It also makes external-call risk easier to isolate.
Auditors still check withdrawal reentrancy and state-update order.
Red flags in code
-
Owed balance is cleared after the external transfer.
-
Withdrawal has no reentrancy protection or safe state ordering.
-
Anyone can redirect another user's withdrawal.
-
Accounting can be credited twice.
-
Tokens with transfer fees are treated as exact-value payouts.
How to test or review it
-
Use a recipient that reverts in
receive(). -
Verify only that recipient's withdrawal fails, not global progress.
-
Confirm owed balance is cleared before transfer.
-
Test reentrant withdrawal attempts.
-
Check events and final balances after successful and failed withdrawals.
Keep learning this topic
Push Payment
Push payment is a payout pattern where a contract sends funds to a recipient during another operation.
Reentrancy
Reentrancy is a smart contract vulnerability where external code calls back into a contract before the first call finishes, often before balances, ownership, or other state has been updated.
Checks-Effects-Interactions
Checks-Effects-Interactions is a Solidity pattern that validates inputs first, updates contract state second, and performs external calls last to reduce reentrancy risk.
Practice this in real audit scenarios
Definitions help, but auditors need reps. SCH turns concepts like Pull Payment into exploit labs, code review habits, and report-writing practice.
Start the free trial or see the full smart contract auditing course.