Payouts & the owed ledger
Bounded-gas pushes, refusals that book instead of revert, and pulling with claim().
The split decides who is owed what; this chapter is how the money actually leaves. One design rule governs all of it: a recipient's behaviour is never allowed to become the pool's problem — refusals book, they don't revert.
The push, bounded
Native legs are pushed with a fixed 30,000-gas stipend, ERC20 legs with a normal transfer — either way a hostile or merely heavy recipient (a blocklist, a reverting receive(), a gas-guzzling callback) cannot brick the harvest or the swap carrying it. The refused amount is booked in a per-(recipient, asset) ledger, identical to the wei to the leg that bounced — nothing is re-derived after a failure.
The owed ledger
Every owed booking is a term of the hook's obligationOf(asset) accounting, and the hook's balance covers that sum at all times — the solvency invariant of the whole delivery system. "Owed" is a parking state, never a haircut.
Why pull-over-push is the right default
The push-first, book-on-refusal shape gives well-behaved recipients zero friction (money just arrives) while making hostile ones pay for their own hostility: the only party inconvenienced by a reverting treasury is the treasury. Compare the alternatives — pushing with full gas lets one recipient revert everyone's harvest; pulling exclusively makes every honest treasury run a claim bot. The hybrid is strictly better than both.
What to index
A dashboard that sums Paid + Owed per recipient reconstructs every wei a program ever distributed; owedOf(to, asset) is the live outstanding balance at any moment.
FAQ
Why did my payout not arrive as a transfer?+
Pushes carry a bounded gas stipend. If your receiver needs more (or reverted), the amount was booked to the owed ledger — call claim() to pull it with your own gas.
Who can call claim()?+
Only the booked recipient, for their own (recipient, asset) balance. There's no admin sweep and no expiry — owed balances wait forever.
What's the difference between owed and parked?+
Owed = harvest payouts refused by a recipient, pulled via claim(). Parked = pot deliveries refused by the pot recipient, retryable BY ANYONE via flushDirect.
Can a booked balance be redirected?+
No — bookings are keyed to the recipient at the moment of the harvest. Changing the config redirects FUTURE harvests only; history is immutable.
Is the venue solvent for these ledgers?+
Yes — the obligation invariant checks that the hook's custody always covers pot balances + carry + owed + parked, across every randomized campaign.