Superfluid CFA Realtime-Balance Conservation

Creating or updating a non-app CFA flow, having its sender close it while their available balance is not below zero, or running the modeled one-level receiver self-delete callback leaves the total value recorded by CFA unchanged.

Superfluid's Constant Flow Agreement (CFA) is the part of the protocol that manages one-to-one token streams. We proved this accounting rule for covered flow creations, updates, ordinary deletions by the sender, and one receiver callback that deletes its own flow. The proof covers CFA accounting, not pools, claims, liquidations, or every possible hook.

Why It Matters

Superfluid balances change continuously, but a flow should only move value between accounts. Creating, changing, or deleting a stream must not make value appear or disappear.

We proved that the sender and receiver changes cancel out, while the other accounts being checked remain unchanged. This stays true as time passes and for the covered one-level callback.

How This Was Modeled and Proven

We modeled the relevant create, update, and delete logic from ConstantFlowAgreementV1.sol at pinned commit 414109689d9041a8b6900b67b947f3f203c1da5d. A CFA projection is the value an account has already settled, plus the value streamed since its last update. Shared settled balances follow SuperfluidToken.sol.

Lean proves that each covered operation preserves the combined CFA projection of the sender and receiver. It also proves that every other account in the supplied list is unchanged. Together, those results preserve the list's total CFA projection, both when the operation happens and at any later modeled time.

For callbacks, we proved that one nested CFA action preserves the same accounting rule when each step is valid. A second callback level is rejected, and a failed nested action rolls the whole operation back. The pinned SelfDeletingFlowTestApp is one concrete verified example. This does not mean that every arbitrary hook contract is verified.

Scope

Covered: successful non-app flow creation and updates; the sender closing their own non-app flow while their available balance is not below zero; and the selected receiver callback that deletes its own flow. The supplied account list cannot contain duplicates and must include both sides of the flow. The property holds at the operation time and later.

Deletion coverage: Superfluid calls an account critical when its available balance is below zero. A third party may then close one of its flows, and the protocol enters a liquidation path with extra payout and possible app-jailing logic. We did not model or prove that path. Our result covers ordinary sender deletion, not every kind of deletion. Liquidation needs a separate model and proof.

Also not covered: Superfluid's complete realtimeBalanceOf calculation across CFA, IDA, and GDA; total supply; pools; claims; minting, burning, upgrades, downgrades, wrapping; arbitrary hooks; or the full Host routing logic. These systems are excluded from this theorem. We do not assume that they work correctly, and we make no claim about them.

The proof applies to the Lean model pinned to the cited source revision. The CFA total uses the same uint256 wraparound as the EVM.

Proof artifacts
  • Contract.lean contains the executable CFA slice and its source scope.
  • Specs.lean defines modularCfaGlobalProjectionSumAt, CfaPairCoveredBy, the callback composition contracts, source relations, and all 22 public properties.
  • Proofs.lean contains the kernel-checked reference proofs.
  • Benchmark PR #140 records the reviewable benchmark change.
FunctionTheoremStatus
finite-list liftpairAndFrame_implies_modular_cfa_global_projectionProven
create, update, deletecreateNonApp_preserves_future_modular_cfa_global_projection
updateNonApp_preserves_future_modular_cfa_global_projection
deleteNonApp_preserves_future_modular_cfa_global_projection
Proven
one-level callbacksuccessfulOneLevel_components_compose
callbackLevelTwo_is_rejected
failedNested_rolls_back_and_prevents_resume
Proven
pinned receiver deletereceiverDeleteCallback_preserves_future_modular_cfa_global_projection
receiverDeleteCallback_matches_factored_instance_behavior
Proven

The case contains 22 public properties: the original 13 local and callback properties plus 9 strengthening tasks shown above. Their reference proofs compile without case-specific axioms, sorry, admit, or native_decide.

Verify it yourself
git clone https://github.com/lfglabs-dev/ethereum-verification-benchmark
cd ethereum-verification-benchmark
git checkout 88c3f45b51156dc9a4905e5f5ea0832851000eeb
lake build Benchmark.Cases.Superfluid.RealtimeBalanceConservation.Proofs

If the build succeeds, Lean has checked all 22 CFA model properties at benchmark commit 88c3f45b51156dc9a4905e5f5ea0832851000eeb.

Assumptions

The first row is the external fact the theorem trusts. The second row records values checked by the modeled operation. CFA-only accounting and liquidation deletion are scope exclusions listed above. We do not assume that the excluded paths work correctly.

  • Host routing is outside this proofThe proof starts after the Host has selected the operation context

    PinnedSourceState and pinnedSourcePathRelation

    Superfluid's Host chooses the token, sender, receiver, flow record, and callback app passed into CFA. The proof checks the CFA accounting after those values have been chosen. It does not prove that the Host selected or encoded them correctly; that would require a separate proof of the Host's routing and context-validation logic.

  • valid runtime valuesTimestamps, flow rates, deposits, and packed values satisfy the source checks

    Executable guards and sourceAccountRelationAt

    These are conditions for a successful call, not assumptions about an excluded part of Superfluid. Invalid calls revert instead of becoming successful flow operations. For the nested callback, we separately proved that failure rolls the entire operation back.

Learn More