increment() first calls Receiver.receive(),
then follows up with a transfer(). If the receive fails, you must not
transfer. If the transfer fails after a successful receive, you may need to
refund or roll back. That coupling — conditioning a follow-up call on the
success of a prior one — is what composable error handling is about.
The “To Panic is Human” chapter handles single-call failure on the
transfer itself. This appendix is the next layer: multi-call flows where
the second call depends on the first.
Two paths: .await and .spawn()
- .await — handle each call inline
- .spawn() — callbacks
The straightforward path: wait for each call, branch on success/failure.Pros: linear, debuggable, reads top-to-bottom. The current activation
waits for the cross-entity round trip.Cons: the entity lock is held longer (until both futures resolve).
When to use which
.await— when the multi-call chain is logically one operation that the caller treats as atomic-ish. Easier to write and debug..spawn()— when the entity should remain available for other activations while the chain finishes. Worth the extra state when the contract is hot.

