web3.path

PHASE 23 distributed protocol boundaries · 14–22 hours

L2, Oracle & Cross-chain Systems

This chapter is about what happens when the system does not progress normally: sequencer downtime, stale prices, duplicated messages, out-of-order delivery, delayed exits, and compromised attesters.

Goal — build a message/state machine with explicit trust assumptions and test its failure transitions, while deploying an L2-aware application.

1. Chain profile before deployment

Write a profile per chain: chain ID, canonical bridge, L1/L2 finality policy, reorg depth, sequencer assumptions, withdrawal path, gas/DA fees, supported opcodes/precompiles, RPC providers, archive/trace requirements, and emergency contacts. Polygon PoS, optimistic rollups, zk rollups, validiums, and appchains must not share one generic “EVM chain” policy.

1b. Light-client vs attestation bridges

ApproachOn-chain verificationRisk
Multisig / oracle networkSignature thresholdKey compromise, collusion
Light clientHeader + proof verificationImplementation bugs, gas cost
ZK proof of stateVerifier contractCircuit/verifier governance

Your lab receiver (labs/phase-23-bridge/) models attestation-style delivery—document which trust model your production integration actually uses.

2. Oracle adapter with a policy boundary

struct PricePolicy { uint32 maxAge; uint8 feedDecimals; uint16 maxDeviationBps; }

function readPrice() external view returns (uint256) {
    (, int256 answer,, uint256 updatedAt,) = primary.latestRoundData();
    if (answer <= 0 || block.timestamp - updatedAt > policy.maxAge) revert Stale();
    uint256 normalized = normalize(uint256(answer), policy.feedDecimals);
    if (deviation(normalized, referencePrice) > policy.maxDeviationBps) revert Deviation();
    return normalized;
}

Document who supplies the reference, whether it is independent, what happens when feeds disagree, how a circuit breaker is reset, and how a sequencer outage is recognized. Never hide these rules in a frontend.

3. Message state machine

enum Status { None, Sent, Delivered, Executed, Failed, Cancelled }
mapping(bytes32 => Status) public status;

function receiveMessage(bytes32 id, bytes calldata payload) external {
    _authenticateTransportAndRemoteSender();
    if (status[id] != Status.Sent) revert InvalidStatus();
    status[id] = Status.Delivered; // consume before arbitrary execution
    _execute(payload);
    status[id] = Status.Executed;
}
ControlFailure it contains
Authenticated endpoint + remote peerforged delivery
Message ID, source chain, nonce, consumed statereplay/duplicate processing
Rate limit, mint cap, circuit breakercatastrophic loss after a bug/key compromise
Idempotent retry/reconciliationtimeouts and provider ambiguity
Full lifecycle monitoring“sent” mistaken for “settled”

4. Practical lab — L2-safe collateral adapter

Deliverable
  1. Deploy a collateral adapter to an L2 testnet. Include block.chainid checks, per-chain config, oracle staleness/decimals/positive-answer validation, and a sequencer-up policy if applicable.
  2. Write tests for stale price, decimal mismatch, outage grace period, fee underestimation, reorg-safe event indexing, and wrong-chain wallet connection.
  3. Build an indexer that uses block hashes, a configurable confirmation depth per chain, rewind/replay logic, and a durable idempotency key.
  4. Produce an operations dashboard with indexer lag, price age, sequencer status, RPC error rate, and failed transaction/message counters.

5. Practical lab — simulated bridge receiver

Deliverable — create two local contracts that model a transport and receiver. Prove with tests that forged sender, wrong source chain, duplicate ID, out-of-order delivery, failed execution, rate-limit exhaustion, and emergency pause behave exactly as your state machine specifies. Do not present this lab as a production bridge.

Relayer ops: docs/RELAYER_RUNBOOK.md. Lab: labs/phase-23-bridge/solution/.

← Phase 22Phase 24: ZK Systems Engineering →