
The decomposition law: why one contract is a monolith and the other is not
An invariant is enforceable only at the finest boundary that can observe both of its terms in one frame. That single rule explains why the staking contract is deliberately monolithic, why the router is decomposed, and — as a corollary — exactly which boundaries leak.
Known formally as The decomposition law in the BlazePhoenix whitepaper.
BlazePhoenix Engineering · updated 2026-08-13 · 11 min · written from the deployed bytecode
By Mitra (@Sigmacrit) — anonymous developer of the BlazePhoenix protocol. The code is the résumé.
Module boundaries in smart contracts are usually argued on taste: separation of concerns, testability, reuse. Those arguments do not survive contact with a substrate where every boundary is an external call and every external call is a window in which state can change. On-chain, a boundary is not an abstraction — it is a frame edge, and a guarantee that straddles one is a guarantee with a gap in it.
BlazePhoenix ships two components with opposite layouts. The routing side is four contracts and one shared library. The staking side is a single contract. Those are not two different philosophies applied inconsistently; they are one rule applied twice, and the rule is worth stating on its own.
The law
An invariant is enforceable only at the finest boundary that can observe both of its terms in the same frame. Everything else about a layout follows from which invariants have to hold and what they need to see at once.
Read as a design procedure rather than as an aphorism, it inverts the usual order of work. You do not decompose a system and then decide where to put the checks. You enumerate the guarantees, write each one as an expression over named state, and let the read set of each expression determine the smallest unit that can carry it. Two terms that must be compared inside one transition must be readable inside one frame; two terms that need never be compared impose no constraint at all.
Applied to the staking contract: the monolith is the guarantee's shape
The central guarantee of the staking contract compares what it holds against what it owes. Holdings are a balance at one address, read from the token contract. Obligations are the sum of several internal quantities, recomputed from storage. Both must be readable in one execution frame for the comparison to mean anything — so the contract that owes must be the contract that holds.
Splitting it would put a network call between the two halves of a solvency check. Everything an external call can do — reenter, revert, return a stale view, be replaced by a different implementation behind the same address — becomes a way for the two halves to disagree. A solvency check that straddles a call boundary is a solvency check with a window in it. The monolith is not a failure to modularise; it is the shape of the property, and any decomposition that looked tidier would have been strictly weaker.
The same law explains a quieter decision inside it: the check is applied at thirteen entry points including both administrative funding paths, rather than at one place downstream. A guard that is not invoked at a site that can change its observable is not a weak guard — it is not a guard. A scope gap is a different failure from silence, and it is the easier of the two to avoid, because it is mechanical: list every site that writes the read set and confirm the guard wraps each one.
Applied to the router: the answer inverts
The router's equivalent guarantee is per-leg: what went into this leg against what came out of it, both observable in the frame that executed the leg. It is established by measuring balance deltas rather than by trusting anything a venue reported, so the read set is the router's own balances before and after a single call. Nothing in that expression requires the discovery registry, the planner and the execution frame to share an address — so they need not, and the deployed-size limit means they had better not.
That is the honest ordering of cause here. The size limit forced the routing side apart; the law says only that the forcing was permissible, because no guarantee the router makes needs the registry and the executing frame in one place. A reader is entitled to be suspicious of an architecture chapter that presents every constraint as a principle, so the split is worth being explicit about: what is chosen is the separation of the registry from the planner, along the state-versus-view axis. What is forced is the separate preview contract, the memory-to-calldata hand-off, and one duplicated dispatch — and that duplication is where every quote-versus-execution divergence not caused by timing actually lives.
There is a clean proof that the size story is a real constraint rather than a retrofitted excuse: the shared library is compiled to inline into its callers, which costs size. That cost was paid deliberately to keep a delegatecall out of the hot path. A project inventing size constraints to justify its layout does not spend size to buy a property.
The corollary is the part worth keeping
The boundaries that can enforce nothing are exactly the boundaries that leak. That is not a separate observation; it is the law read backwards. A surface which observes only one term of a guarantee cannot hold that guarantee, and therefore restates it — and a restatement drifts, because nothing fails when it does.
The protocol has two instances of the corollary and they are the same structural fact seen twice. The read-only preview surface enforces nothing. Of everything it returns, exactly one number binds — the minimum the caller supplies — and it binds only because the caller passes it again at execution time and the router checks it against the recipient's actual balance change. The floor the preview reports is taken from the caller's own route data rather than derived by the protocol; the field named as the effective minimum omits the protocol floor entirely; a gas estimate is consumed by nothing; two route-classification fields are fixed zeros; and a field named as a safety buffer has no counterpart in the router at all. Every one of those is a guarantee stated where it cannot be enforced.
The second instance: the solver's protections apply only inside the solver. A route assembled by hand and submitted through the calldata entry point receives none of the believability filtering and none of the capacity clamping, because those live in a contract that path does not call. The protections are real and they are not properties of the protocol — they are properties of one entry point, and describing them as protocol-wide would be the same drift in a different file.
A frame boundary that produced a real defect
The law has a cost, and the cost showed up as a funds-affecting defect worth setting out because each link in the chain is ordinary. The deployed-size limit forced the on-chain-solving path to hand its result across an external self-call, because a route computed in memory cannot be passed directly to a function expecting calldata. The self-call created a new frame. Inside that frame the caller is the contract itself. The refund of unspent input — written, in the obvious way, as a transfer back to the caller — therefore became a transfer to the contract, and the user's change stayed where it was, recoverable only through the administrative rescue path.
No step in that chain is a mistake. The composition is. It is also the law biting the other way: the frame boundary that was harmless for every invariant we had written down was not harmless for a property nobody had written down at all — that unspent input returns to the person who supplied it. A fix exists, passing the payer explicitly rather than inferring it from the calling frame, and at the time of writing it had not reached the published release. The calldata path is unaffected, because there the caller is the user.
How to use this in a review
The procedure is short enough to run on any contract system, ours included. For each guarantee, write the expression, not the sentence — if the guard is a modifier, the invariant is what the modifier computes, not what its comment claims. Take the read set. Find the smallest deployed unit whose frame contains it; that is where the check belongs, and if the check lives anywhere coarser, ask what happens in the gap. Then list every site that can write the read set and confirm the check wraps each one.
Finally, look at every boundary that enforces nothing and treat every guarantee-shaped sentence emitted across it as documentation rather than as behaviour. In our own system that rule flagged the preview surface before any specific defect in it was known, which is the argument for the rule: it is generative. It is not evidence that the rule is true, and there is no control group. It turned "think about where the checks go" into an enumeration with a stopping condition, and that is the whole of the claim.
Do not trust this page — reproduce it
Every claim above is checkable against the chain. Start here:
take any guarantee stated in our documentation, write it as an expression over named state, and check that every contract that can write a term of that expression is inside the frame where the check runs — the preview surface fails this test by construction, which is why nothing it returns bindsCite this article
Licensed CC BY 4.0 — quote, translate and reuse freely, including commercially, with attribution and a link. Copy a ready-made citation:
BlazePhoenix (2026). The decomposition law: why one contract is a monolith and the other is not. BlazePhoenix Engineering. https://blazephoenix.xyz/learn/invariant-decomposition-law@misc{blazephoenix_invariant_decomposition_law,
title = {The decomposition law: why one contract is a monolith and the other is not},
author = {BlazePhoenix},
year = {2026},
url = {https://blazephoenix.xyz/learn/invariant-decomposition-law},
note = {Accessed: reproduce the claim with the command above}
}Writing an answer, a wiki entry or a paper? The claim above is reproducible against the chain before you quote it — which is the only sound basis for citing a technical source at all.
Contracts are verified on every chain we deploy to — addresses in the protocol manifest. Deeper formal treatment: the whitepaper (PDF).
Share this article · join the discussion