Skip to content

docs: circle failure handling spec - #188

Open
franrolotti wants to merge 2 commits into
devfrom
feat/circle-failure-handling-spec
Open

franrolotti wants to merge 2 commits into
devfrom
feat/circle-failure-handling-spec

Conversation

@franrolotti

@franrolotti franrolotti commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

Spec only — no executable changes. Adds docs/circle-failure-handling.md.

Why

Today a missed deposit has exactly one outcome: any single member calls decommission, the circle dies permanently, and the contract pushes every unclaimed round's deposits back in an O(n²) loop. Three problems with that:

  • It does not scale, and BREAD makes it worse. Up to members × rounds individual transfers in one transaction. BREAD is ERC20VotesUpgradeable and overrides transfer/transferFrom to auto-delegate on receipt, so each transfer pays for voting-checkpoint writes on both sides, an external self-call to this.delegates(recipient), and a full _delegate the first time an address receives BREAD — several times a vanilla ERC20 transfer. The spec asks for a fork-based gas benchmark to pin down the largest circle the current path can actually wind down.
  • One member pays for everyone. Whoever calls decommission funds the entire refund distribution out of their own pocket. Pull-based settlement makes each member pay for their own exit. Holds regardless of any gas limit.
  • It does not make anyone whole. Deposits that already funded a paid-out pot are gone. In a 3-member circle where A and B claim and A then stops paying, C is down two full deposits and A walks away net positive.

On token risk: the usual argument for pull payments — a recipient that can permanently revert a push and brick the loop for everyone — does not apply to this deployment. BREAD has no blacklist, no pause, and no transfer hooks. The spec says so explicitly and does not lean on it. It is retained only as defense in depth, since setTokenAllowed exists and this is an upgradeable long-lived contract.

What the spec proposes

A graded response — pause → cure → eject → halt — backed by collateral, with all value transfers pull-based. Four phases, shipped in order:

  1. Pull settlement — halt (O(1)) + per-member settle. Independent of everything else; ships alone.
  2. Pause / resume — the round clock stops being a pure function of block.timestamp. Forces AutomaticSavingCircles to stop duplicating the round math and call shared views instead.
  3. Collateral — per-member bond posted before start, forfeited on ejection.
  4. Ejection — remove a defaulter without compacting circleMembers (indices are payout rounds), with the seized bond backfilling the stalled round first.

Phases 1–3 are each independently shippable; phase 4 is optional and can be dropped without stranding the others.

Two design consequences worth reviewer attention:

  • The pot formula changes. depositAmount × circleMembers.length breaks once a bond can backfill a shortfall or a member can be ejected mid-rotation. Replaced by roundTotal[id][r] — a round pays out exactly what it collected.
  • Power split on grace. The pause is the grace window, with no timer; the circle owner ends it. Members retain unilateral exit via halt for as long as the circle is paused, which is what makes an unbounded owner-controlled grace safe — a passive owner can stall the circle but can never trap the funds. No weaker than today, where any single member can decommission unilaterally.

What this PR needs from reviewers

The point of merging this is to close §8. Two questions block later phases and can't be answered by one person:

  • §8.1 Bond tier — flat depositAmount (covers one missed round, cheap) vs position-scaled depositAmount × (n − 1 − memberIndex) (full collateralization, closes the "claim early then default" hole, expensive in locked capital). Blocks phase 3.
  • §8.6 Owner succession — if the ejected member is the circle owner, nobody can resume the circle. Spec recommends passing ownership to the lowest-index non-ejected member, since auto-halting would mean an owner's default kills the circle — exactly what ejection exists to prevent. Blocks phase 4.

Also open: repeat-default handling (§8.2), bond top-up after backfill (§8.3), an absent-owner backstop so a stalled circle can continue rather than only die (§8.4), and whether halt during a pause should need a quorum (§8.5).

Phase 1 depends on none of these and can start in parallel with this review.

Replaces the single terminal decommission path with a graded response
(pause -> cure -> eject -> halt) backed by collateral, and makes all
value transfers pull-based.

Decided: ejection is permissionless once the owner ends the grace; the
pause is the grace window and has no timer. Bond sizing and repeat
default handling are left open.
BREAD is ERC20VotesUpgradeable with no blacklist, pause, or transfer
hooks, so a recipient cannot permanently revert a push transfer. Drop
the fund-locking claim and the reverting-recipient claim, which was
wrong for any plain ERC20.

Phase 1 now rests on gas and cost fairness: BREAD's auto-delegating
transfer override makes each transfer several times the cost of a
vanilla ERC20, and push means one member funds everyone's refund.
Token-restriction risk is retained explicitly as defense in depth.

Adds a token assumptions section and a fork-based gas benchmark against
real BREAD, since MockERC20 understates transfer cost.
@Mettodo

Mettodo commented Sep 3, 2026

Copy link
Copy Markdown

I think that we should be looking at the "social layer" too (not only at "mathematical" solutions)

A key insight from my conversations with Jean Claude (Winkomun & Paddle) is that he observed how

savings circle members would look for jobs for people with debts: because they care about their friends and because they want debts to be repaid

I got a similar comment after demoing Stacks to RuDee from https://www.equitypicture.com/. They asked: would it be possible to pause the Stacks (vs decommissioning)?

I personally also feel the same way regarding our failed Stacks: how can we resume /finish it? Aligned with https://discord.com/channels/649359982424358952/1418564434367873024/1544765137385160796

@Mettodo

Mettodo commented Sep 3, 2026

Copy link
Copy Markdown

I mentioned "CRC as collateral" as a joke, but it can be an interesting avenues to explore

  • Users currently get around $7/month worth of CRC. Simple maths show that, e.g. in our case, we could have pooled over $100 ($7/month x 10 users x 1,5 months).
  • The trust graph is a way to create a decentralized identity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants