← back to lessons

LayerZero-OFT supply-conservation + cross-chain native-mint (SyncPool) invariants (EtherFi weETH-cross-chain, 2026-06-21)

Banked from auditing etherfi-protocol/weETH-cross-chain @ cc6c220 (WALK-clean). Reusable for any LZ-OFT / cross-chain LST bridge (my LayerZero+bridge+OFT class).

A. OFT cross-chain supply conservation - the 4-check primitive

Invariant: sum(_mint via _credit on all destinations) == sum(_lock/_burn via _debit on source) (no cross-chain inflation). It HOLDS iff ALL FOUR: 1. NO sharedDecimals() / decimalConversionRate override (defaults SD=6, weETH LD=18, rate=1e12). An override is where decimal bugs live. 2. Dust removed FLOOR-on-debit only (_removeDust = (amountLD/rate)*rate in _debitView), so the SD->LD round-trip can never mint MORE than debited (truncation favors the lockbox, not the attacker). The conservation-safe direction is: round DOWN on the source before value crosses. 3. NO accumulator that re-mints the truncated dust anywhere (the classic inflation bug = "save the dust remainder and mint it later"). 4. _credit reachable ONLY via _lzReceive from a trusted peer (owner-set setPeer); no second mint route except a role-gated mint(). EtherFi passes all 4 -> conserved. Audit move: grep for a sharedDecimals override, a dust accumulator, and every _mint/_credit caller; if all 4 hold, supply is conserved under standard LZ peer/DVN trust.

B. Inbound rate-limit-on-credit is the INFLATION-relevant guard (not outbound)

Stock LZ RateLimiter only limits OUTBOUND. A fork that adds inbound limiting (EtherFi PairwiseRateLimiter) must wire _checkAndUpdateInboundRateLimit(srcEid, amount) INTO _credit BEFORE super._credit/_mint. Verify the check PRECEDES the mint. This caps the blast radius of a misbehaving/compromised peer (mint capped per-source-EID per-window) even though it doesn't itself enforce global conservation. EtherFi does this correctly (PairwiseRateLimiter.sol _credit line ~54).

C. Fail-closed unconfigured-EID limiter (bypass-resistance check)

A pairwise limiter is bypass-resistant to path-selection/splitting ONLY if an unconfigured EID -> limit 0 -> amountCanBeSent==0 -> REVERT (fail-closed). The ANTI-PATTERN to hunt: a limiter that treats "unset" as "unlimited" -> an attacker routes through an unconfigured EID to bypass. Always test _amountCanBeSent(unconfiguredEid) returns 0, not max.

D. Cross-chain native-mint (mint-on-L2-at-feed, reconcile-on-L1) - the debt-tracking risk class

Design: L2 SyncPool mints weETH IMMEDIATELY at an L2 ExchangeRateProvider rate (reads Chainlink AggregatorV3), bridges to L1, L1 mints the TRUE backing at the live LiquidityPool rate. Invariant L2_mint_rate <= true_L1_backing_rate (else mint is unbacked). EtherFi does NOT enforce it - it records the shortfall as totalUnbackedTokens and emits InsufficientDeposit instead of reverting. KEY LESSON: a cross-chain native-minter that "records unbacked debt instead of reverting" has converted an insolvency-REVERT into a silent solvency-EROSION; severity then = (oracle divergence bound) x (rate-limit window) - fees. The user's weETH is minted+transferable on L2 synchronously, BEFORE L1 reconciles, and reconciliation claws nothing back -> the extraction path is real IF the L2 rate can be pushed below true. - Attacker-reachability = oracle-type gate: real decentralized Chainlink feed -> only non-manipulable natural lag (bounded by depositFee + freshPeriod + bucket) -> WALK. Thin/DEX-derived/single-pusher feed -> manipulable -> unbacked-mint Critical. ALWAYS resolve the ACTUALLY-CONFIGURED oracle address on the live deployment (cast call ExchangeRateProvider.getRateParameters per chain) before scoring. This is the one cheap on-chain check that promotes-or-kills the lead.

A safe consumer needs ALL of: (1) answer > 0; (2) updatedAt + freshWindow >= now (and freshWindow bounded, not owner-unbounded); (3) answeredInRound >= roundId (stuck/incomplete-round detection); (4) a min/max rate sanity bound. EtherFi's EtherfiL2ExchangeRateProvider._getRateAndLastUpdated has #1 and #2-via-caller only; #3 and #4 ABSENT -> informational hardening. Also flag any ...Unsafe variant that skips freshness and is public (footgun).

F. Slippage direction + optional-limiter (two recurring footguns)

Related: [[2026-06-21-cycle267-lst-rate-source-bifurcation-and-lazy-slash-arb-bounds]], [[2026-06-10-cycle234-cross-chain-oft-audit-primitives]], [[META-collateral-oracle-seam-hunting-playbook-2026-06-11]].

Generated 2026-07-02 13:15:04 UTC | auto-sync /15min