ISOCHRON (Interconnected Sequencing Oracle for Cross-chain Harmonized Reliability, Ordering & Network)
A universal shared sequencing layer for multiple rollups, enabling atomic cross-chain execution, unified liquidity, fair MEV distribution, and sovereign chain policies. The core framework is rollup-agnostic with chain-specific adapters (OP Stack, Arbitrum Nitro, Generic EVM), arbitrator-based governance (Kleros default), and a Rust relay for time-sensitive bundle processing.
ISOCHRON is a universal shared sequencing layer that manages sequencing authority for multiple rollups from a single Hub contract. It enables atomic cross-chain bundle execution, sovereign per-chain policies, and fair MEV distribution while preserving individual chain sovereignty through opt-in rotation and SLA-based governance.
The default block building mechanism is MEV-Boost + Flashblocks (private mempool), but the architecture is fully pluggable - chains can opt into public mempool, encrypted mempool, or custom building mechanisms via the BuilderRegistry.
Key Features:
- Hub-and-Spoke Architecture: Single Hub manages multiple L2 chains atomically
- Atomic Cross-Chain Bundles: Multi-chain transaction bundles with optimistic state atomicity via on-chain execution wrappers (AtomicBundleExecutor) and fraud-provable outcome verification
- Universal Chain Support: Adapters for OP Stack, Arbitrum Nitro, and any EVM rollup (Cosmos planned)
- Sovereign Policies: Each chain declares its own sequencing rules (ordering, MEV, timing, inclusion)
- Deterministic Fraud Proofs: On-chain verifiable proofs for timing, ordering, inclusion, and bundle violations
- Kleros Arbitration: Subjective criteria (MEV violations) escalated to decentralized arbitration
- Pluggable Builders: MEV-Boost + Flashblocks default, upgradeable to any building mechanism
- Rust Relay: Time-sensitive bundle processing, validation, and policy enforcement
- Active Handoff Protocol: Zero-downtime proposer transitions with grace period
- Scalable: Sharded rotation supports thousands of chains
- No Trusted Setups: No hardcoded TEEs (chains may opt-in via sovereign policy)
ISOCHRON governance is anchored by formal policies enforced through an arbitrator (Kleros is the default arbitrator today):
| Policy | Description |
|---|---|
| Sequencer Policy | Service-level requirements for sequencer operators |
| Adapter Policy | Acceptance criteria for rollup adapters |
| Chain Registry Policy | Acceptance/removal criteria for ISOCHRON chain integration |
These policies define:
- Acceptance Criteria: Requirements for registration (Sybil resistance, operational readiness)
- Service-Level Requirements: Grounds for removal (missed handoffs, liveness failures)
- Evidence Standards: How violations are proven
git clone <repository-url>
cd op
./start.shThis starts L1 with governance contracts:
- L1: Local Anvil chain (localhost:8545)
- Governance: SharedSequencerHub with mock arbitrator registry
- Operators: 3 test operators registered and ready for rotation
To run the complete L2 with op-geth, op-node, and op-batcher:
./start.sh l2This starts:
| Service | URL | Description |
|---|---|---|
| L1 RPC | http://localhost:8545 | Anvil (chain ID: 31337) |
| L2 RPC | http://localhost:9545 | op-geth (chain ID: 42069) |
| L2 WS | ws://localhost:9546 | WebSocket endpoint |
| Rollup RPC | http://localhost:9547 | op-node |
# Check L2 is running
cast chain-id --rpc-url http://localhost:9545
# Send ETH on L2
cast send --rpc-url http://localhost:9545 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--value 0.1ether 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
# Check L2 block number
cast block-number --rpc-url http://localhost:9545./start.sh test./start.sh status # Show L1 and L2 status
./start.sh logs # Stream logs from all services
./start.sh stop # Stop all services
./start.sh clean # Clean all data and start fresh# Install Foundry
curl -L https://foundry.paradigm.xyz | bash && foundryup
# Install dependencies
forge install
# Build contracts
forge build
# Run all Solidity tests (228 tests across 9 test suites)
forge test
# Build Rust relay
cd relay && cargo build --release
# Run Rust relay tests
cd relay && cargo testThe ISOCHRON uses a Hub-and-Spoke model anchored by a ProposerRegistry:
ISOCHRON HUB-AND-SPOKE ARCHITECTURE
┌────────────────────────────────────────────────────────────────────────────┐
│ L1 (Ethereum) │
│ │
│ ┌─────────────────────┐ │
│ │ ProposerRegistry │ │
│ │ "The Dumb Pipe" │ │
│ │ │ │
│ │ - Top-N DPoS │ │
│ │ - Liveness Focus │ │
│ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────┐ │
│ │ SharedSequencerHub │ │
│ │ (Central Authority) │ │
│ │ │ │
│ │ - rotateNetwork() │ ◄── Atomic Multichain Rotation │
│ │ - connectChain() │ │
│ │ - rotateShard() │ │
│ └──────────────┬───────────────┘ │
│ │ │
│ ┌──────────┼──────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ RollupConfig│ │ RollupConfig│ │ RollupConfig│ (Spokes) │
│ │ Chain A │ │ Chain B │ │ Chain C │ │
│ └────────────┘ └────────────┘ └────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────────────┘
ATOMIC ROTATION
┌────────────────────────────────────────────────────────────────────────────┐
│ │
│ rotateNetwork() updates ALL chains in a single transaction: │
│ │
│ for (chain in connectedChains) { │
│ calls = adapter.getRotationCalldata(chain.rollupConfig, rotationData) │
│ for (call in calls) { rollupConfig.call(call) } │
│ } │
│ │
│ Gas: ~60k per chain | Max: ~450 chains per block at 30M gas limit │
│ │
└────────────────────────────────────────────────────────────────────────────┘
The universal sequencing layer extends the Hub-and-Spoke model with four interconnected subsystems:
UNIVERSAL SEQUENCING LAYER
┌──────────────────────────────────────────────────────────────────────┐
│ SharedSequencerHub │
│ (Central Nervous System) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ Bundle │ │ Builder │ │ Policy │ │ Fraud │ │
│ │ Registry │ │ Registry │ │ Manager │ │ Proof │ │
│ │ │ │ │ │ │ │ Verifier │ │
│ │ Cross-chain │ │ MEV-Boost + │ │ Per-chain │ │ Timing, │ │
│ │ atomic │ │ Flashblocks │ │ sovereign │ │ ordering │ │
│ │ bundles │ │ (default) │ │ sequencing │ │ inclusion│ │
│ │ │ │ │ │ rules │ │ proofs │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬───────┘ └────┬─────┘ │
└─────────┼───────────────┼───────────────┼──────────────┼──────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌───────────┐ ┌──────────────┐
│ BundleEscrow │ │ Flashblocks │ │ Default │ │ Kleros │
│ │ │ Builder │ │ Policy │ │ Arbitrator │
│ Tips, bonds, │ │ │ │ │ │ │
│ slashing │ │ Private │ │ FCFS + │ │ Subjective │
│ (10% reward) │ │ mempool │ │ sandwich │ │ dispute │
│ │ │ relay │ │ protection│ │ resolution │
└──────────────┘ └──────────────┘ └───────────┘ └──────────────┘
Subsystem Overview:
| Subsystem | Contract | Purpose |
|---|---|---|
| Bundle Execution | CrossChainBundleRegistry + BundleEscrow + AtomicBundleExecutor |
Atomic multi-chain bundles with optimistic state atomicity and tip/bond escrow |
| Block Building | BuilderRegistry + FlashblocksBuilder |
Pluggable builders, MEV-Boost + Flashblocks default |
| Sovereign Policy | SovereignPolicyManager + DefaultPolicy |
Per-chain sequencing rules and compliance checking |
| Fraud Proofs | FraudProofVerifier |
Deterministic proofs + Kleros escalation |
Bundles allow atomic multi-chain transaction execution with optimistic state atomicity guarantees. The system uses two layers of enforcement:
- L2 Execution Layer (
AtomicBundleExecutor): Deployed on every Spoke chain, wraps bundle operations so outcomes are always recorded on-chain — even when the inner call reverts. - L1 Verification Layer (
CrossChainBundleRegistry+FraudProofVerifier): Tracks commitments and verifies atomicity across chains via fraud proofs.
ATOMIC CROSS-CHAIN BUNDLE LIFECYCLE
User/Searcher Relay/Sequencer AtomicBundleExecutor BundleRegistry
───────────── ─────────────── ──────────────────── ──────────────
│ │ │ │
│ 1. Submit bundle │ │ │
│ (via relay API) │ │ │
│───────────────────▶│ │ │
│ │ │ │
│ │ 2. Simulate all ops │ │
│ │ off-chain │ │
│ │ (reject if any fail)│ │
│ │ │ │
│ │ 3. commitBundle() │ │
│ │─────────────────────────────────────────▶ │
│ │ │ │
│ │ 4. executeBundle() │ │
│ │ on Chain A │ │
│ │─────────────────────▶│ │
│ │ emit BundleResult │ │
│ │ (bundleId, success)│ │
│ │ │ │
│ │ 5. executeBundle() │ │
│ │ on Chain B │ │
│ │─────────────────────▶│ │
│ │ emit BundleResult │ │
│ │ (bundleId, success)│ │
│ │ │ │
│ │ 6. confirmChainExecution() per chain │
│ │─────────────────────────────────────────▶ │
│ │ │ │
│ │ 7. completeBundle() │ │
│ │─────────────────────────────────────────▶ │
│ │ │ │
ATOMICITY VIOLATION PATH:
Challenger detects: Chain A BundleResult(success=true)
Chain B BundleResult(success=false)
│
│ Submit AtomicityViolation fraud proof to L1 FraudProofVerifier
│ with Merkle proofs of both BundleResult event logs
│───────────────────▶ FraudProofVerifier verifies statusA ≠ statusB
│ → Sequencer slashed
Bundle States: Committed → Executed (success) or Expired / Violated / Cancelled (failure)
Atomicity Model: Optimistic State Atomicity
Unlike pure economic atomicity (where only bonds are at risk), ISOCHRON enforces optimistic state atomicity through the AtomicBundleExecutor:
- Before execution: The sequencer simulates all bundle operations off-chain. If any operation would fail, the entire bundle is excluded from all chains.
- During execution: Operations are routed through
AtomicBundleExecutor.executeBundle()on each chain. The executor wraps calls so that even if the inner logic reverts, the outer transaction succeeds and emits aBundleResultevent recording the outcome. - After execution: Anyone can verify that
BundleResultevents for the samebundleIdhave consistentsuccessstatus across all chains. If they differ, a fraud proof is filed and the sequencer is slashed.
This eliminates the "broken leg" problem where a user ends up with a partial cross-chain position (e.g., bought on Chain A but the corresponding sell on Chain B reverted).
Economic Guarantees:
- Sequencer posts a bond when committing bundles
- Tips incentivize timely execution
- Violated bundles trigger bond slashing (10% to reporter, 90% to governance treasury)
- Atomicity violations (mismatched results across chains) trigger immediate slashing
- Expired bundles can be cleaned up by anyone after deadline
Each chain can declare its own sequencing rules that the active sequencer must obey:
SOVEREIGN POLICY ARCHITECTURE
Chain A Governance SovereignPolicyManager FraudProofVerifier
────────────────── ────────────────────── ──────────────────
│ │ │
│ declarePolicy( │ │
│ chainId: A, │ │
│ ordering: FCFS, │ │
│ enforcement: Hybrid, │ │
│ maxBlockTime: 2s, │ │
│ sandwichProtection: true │ │
│ ) │ │
│─────────────────────────────▶│ │
│ │ │
│ │ Policy violation? │
│ │───────────────────────────▶│
│ │ │
│ │ Deterministic proof │
│ │ (timing, ordering, │
│ │ inclusion) OR │
│ │ Kleros escalation │
│ │ (MEV, subjective) │
Ordering Strategies:
| Strategy | Description |
|---|---|
SequencerDiscretion |
Sequencer chooses order freely (default) |
PriorityFee |
Order by gas price / priority fee |
FCFS |
First-come-first-served ordering |
Custom |
Delegated to a custom policy contract |
Enforcement Types:
| Type | Description |
|---|---|
Deterministic |
On-chain verifiable (timing, ordering, inclusion) |
Subjective |
Requires human judgment (MEV violations) → Kleros |
Hybrid |
Deterministic where possible, Kleros for the rest |
The FraudProofVerifier supports two verification paths:
Deterministic Proofs (trustless, on-chain):
| Proof Type | Verification Logic |
|---|---|
TimingViolation |
Block gap exceeds chain's maxBlockTime |
OrderingViolation |
FCFS misordering (earlier nonce sequenced later) |
InclusionViolation |
Transaction censored past forcedInclusionDeadline |
BundleViolation |
Committed bundle deadline passed without execution |
Subjective Proofs (Kleros arbitration):
| Proof Type | Escalation |
|---|---|
MEVViolation |
Sandwich attacks, front-running when policy prohibits |
CustomViolation |
Chain-specific rule violations |
UnjustifiedPause |
Circuit breaker pause was not justified (disputed via Kleros) |
Challenge Flow:
- Challenger posts bond (0.5 ETH default) with proof data
- For deterministic types:
verifyDeterministicProof()resolves immediately - For subjective types:
escalateToArbitration()creates Kleros dispute - If no response within 24h: auto-accepted (challenger wins)
- Bond returned to winner, slashed from loser
ISOCHRON uses adapters for plug-and-play integration without modifying member chains:
| Adapter | Chain Type | Rotation Mechanism |
|---|---|---|
OpStackAdapterV1 |
OP Stack (Bedrock/Ecotone) | setBatcherHash() + setUnsafeBlockSigner() |
ArbitrumAdapterV1 |
Arbitrum Nitro | setIsBatchPoster() on SequencerInbox |
GenericAdapterV1 |
Any EVM rollup | Arbitrary function calls (single or multi-call) |
The GenericAdapterV1 supports two modes:
- Single-call:
abi.encode(bytes4 selector, bytes callData)- calls one function - Multi-call: prefix
0xFF+abi.encode(bytes4[] selectors, bytes[] callDatas)- calls multiple functions atomically
The BuilderRegistry manages approved block builders with per-chain overrides:
BuilderRegistry
├── defaultBuilder: FlashblocksBuilder (MEV-Boost + Flashblocks)
├── chainBuilders:
│ ├── Chain 10 (OP): FlashblocksBuilder
│ ├── Chain 42161 (Arb): FlashblocksBuilder
│ └── Chain 8453 (Base): CustomBuilder (future)
└── builderTypes:
├── PrivateMempool (Flashblocks, MEV-Boost)
├── PublicMempool (standard building)
├── EncryptedMempool (threshold encryption, future)
└── Custom (chain-specific)
Each builder validates build requests (chain support, gas limits, bundle count, timestamps) and exposes a relay endpoint for discoverability.
The isochron-relay crate handles time-sensitive components that benefit from Rust's performance:
relay/
├── src/
│ ├── main.rs # Entry point, initializes all components
│ ├── config.rs # TOML-based relay configuration
│ ├── bundle/
│ │ ├── types.rs # Bundle, operation, commitment types
│ │ ├── validator.rs # Bundle validation (ops, deadline, chains, gas)
│ │ └── sequencer.rs # Bundle lifecycle management
│ ├── chain/
│ │ └── mod.rs # Chain adapter registration and management
│ ├── policy/
│ │ └── mod.rs # Real-time policy compliance engine
│ └── relay/
│ └── mod.rs # HTTP API server (health, bundles)
API Endpoints:
| Method | Path | Description |
|---|---|---|
GET |
/health |
Health check and bundle count |
POST |
/bundles |
Submit a cross-chain bundle |
GET |
/bundles/:id |
Query bundle status |
ISOCHRON provides a decentralized onboarding path for new chains via the ChainRegistry (GeneralizedTCR):
CHAIN INTEGRATION FLOW
Chain Team ChainRegistry SharedSequencerHub
─────────── ───────────── ──────────────────
│ │ │
│ 1. Deploy rollup chain │ │
│ with config contract │ │
│ │ │
│ 2. Register via │ │
│ ChainDeploymentKit │ │
│─────────────────────────────▶│ │
│ + deposit + metadata │ │
│ │ │
│ │ 3. Challenge Period │
│ │ (Community curation) │
│ │ │
│ │ 4. Status: Registered │
│ │────────────────────────────▶│
│ │ │
│ │ 5. Hub connects chain │
│ │ connectChainFromRegistry│
│ │ │
│ 6. Chain is now part of ISOCHRON! │
│ Atomic rotation enabled │
│ │ │
Key Components:
| Component | Type | Purpose |
|---|---|---|
| ChainRegistry | GeneralizedTCR | Decentralized chain application registry |
| ChainDeploymentKit | Helper | Simplified interface for chain teams |
ChainRegistry vs PermanentGTCR:
Unlike the operator registries (which use PermanentGTCR with permanent stakes), the ChainRegistry uses a standard GeneralizedTCR where:
- Deposits are returned after successful registration
- No perpetual stake requirement
- Focus on chain metadata validation, not ongoing operational compliance
- Community can challenge invalid applications during challenge period
Registration Requirements:
- Valid rollup deployment with accessible configuration contract (e.g., OP Stack SystemConfig)
- Chain team has operational capability
- No duplicate chain IDs
- Alignment with ISOCHRON sequencer SLA requirements
Separating stake ownership from operational keys improves security:
| Role | Description | Registry Field |
|---|---|---|
| Staker (Owner) | Holds governance stake, can update keys | item.submitter |
| Batcher | Posts batches to L1 (hot key) | itemKeys[itemID].batcher |
| Unsafe Signer | Signs P2P unsafe blocks (hot key) | itemKeys[itemID].unsafeSigner |
Recommendation: Use different addresses for Staker vs Operational Keys. If hot keys are compromised, the governance stake remains safe.
CRITICAL: OP Stack sequencer authority requires TWO keys rotated together:
| Key | Purpose | SystemConfig Function |
|---|---|---|
| Batcher | Posts batches to L1 | setBatcherHash() |
| Unsafe Signer | Signs P2P unsafe blocks | setUnsafeBlockSigner() |
Both keys are registered with each proposer and rotated atomically across ALL chains by the Hub via adapters.
PROPOSER ROTATION FLOW (ISOCHRON)
┌───────────────────────────────────────────────────────────────────────────┐
│ L1 (Ethereum) │
│ │
│ ProposerRegistry SharedSequencerHub │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ DPoS Selection │ │ Central Authority │ │
│ │ │ │ │ │
│ │ Top-N Active Set │ ◀──────────▶ │ currentProposer │ │
│ │ ├─ stake │ │ currentEpoch │ │
│ │ └─ operationalKey │ │ connectedChains[] │ │
│ │ │ │ │ │
│ │ selectNextProposer │ │ rotateNetwork() │ │
│ └─────────────────────┘ └──────────┬──────────┘ │
│ │ │
│ ┌───────────────────────────────────┼─────────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ │ SystemConfig │ │ SystemConfig │ │ SystemConfig │
│ │ Chain A │ │ Chain B │ │ Chain C │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
└───────────────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────┼─────────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐
│ Proposer A │ │ Proposer B │ │ Proposer C │
│ │ │ │ │ │
│ ┌───────────────┐ │ │ ┌───────────────┐ │ │ ┌───────────────┐ │
│ │ Proposer │ │ │ │ Proposer │ │ │ │ Proposer │ │
│ │ Agent │ │ │ │ Agent │ │ │ │ Agent │ │
│ └───────┬───────┘ │ │ └───────┬───────┘ │ │ └───────┬───────┘ │
│ │ │ │ │ │ │ │ │
│ ┌───────▼───────┐ │ │ ┌───────▼───────┐ │ │ ┌───────▼───────┐ │
│ │ op-node │ │ │ │ op-node │ │ │ │ op-node │ │
│ │ op-batcher │ │ │ │ op-batcher │ │ │ │ op-batcher │ │
│ │ (stopped) │ │ │ │ (ACTIVE) │ │ │ │ (stopped) │ │
│ └───────────────┘ │ │ └───────────────┘ │ │ └───────────────┘ │
└───────────────────┘ └───────────────────┘ └───────────────────┘
Each proposer MUST run a proposer agent that:
- Monitors
isCurrentProposer(address)on SharedSequencerHub - Starts local op-node sequencer + op-batcher when becoming active
- Stops them when no longer active
- Calls
rotateNetwork()during grace period
This is an SLA requirement - proposers that produce blocks while unauthorized can be challenged and removed.
See agent/ for a reference implementation.
The Active Handoff protocol ensures zero-downtime operator transitions with no L2 re-orgs. It uses a 3-Phase State Machine:
ACTIVE HANDOFF 3-PHASE STATE MACHINE
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ Phase 1: PROTECTED Phase 2: VOLUNTARY Phase 3: FORCED
│ (0 → epochDuration) (+ GRACE_PERIOD) (Dead Man's Switch)
│ │
│ ┌─────────────────────┐ ┌───────────────────────┐ ┌───────────────┐ │
│ │ Nobody can rotate │ │ ONLY current operator │ │ Anyone can │ │
│ │ Standard operation │ │ can trigger rotation │ │ force rotate │ │
│ │ │ │ │ │ │ │
│ │ Operator produces │ │ Operator: │ │ Liveness │ │
│ │ blocks normally │ │ 1. Stops sequencing │ │ fallback if │ │
│ │ │ │ 2. Flushes batches │ │ operator is │ │
│ │ │ │ 3. Calls rotate() │ │ unresponsive │ │
│ └─────────────────────┘ └───────────────────────┘ └───────────────┘ │
│ │
│ Time: 0 ─────────────── epochDuration ────────── +GRACE_PERIOD ────▶ │
│ (1 hour) (10 minutes) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
3-Phase Rotation:
| Phase | Time Window | Who Can Rotate | Purpose |
|---|---|---|---|
| 1 - Protected | 0 → epochDuration |
Nobody | Standard operation |
| 2 - Voluntary | epochDuration → +GRACE_PERIOD |
Current Operator Only | Flush batches, then rotate atomically |
| 3 - Forced | After Phase 2 | Anyone | Dead Man's Switch (liveness fallback) |
The grace period ensures the outgoing operator can flush all pending batches to L1 before triggering rotation, preventing any transaction orphaning.
Handoff Sequence (Operator Agent):
- Monitor: Watch for epoch end approaching
- Prepare: Stop accepting new transactions
- Flush: Force
op-batcherto submit all pending unsafe blocks to L1 - Rotate: Call
rotateNetwork()(ideally in same tx or immediately after batch confirms) - Handover: New operator's agent sees L1 state change and immediately starts sequencing
Constants:
GRACE_PERIOD = 600(10 minutes)
- Proposers Register: Submit stake to ProposerRegistry with operational keys
- DPoS Selection: Top-N stakers (by own + delegated stake) are in the active set
- Chains Register: Chains apply to ChainRegistry via ChainDeploymentKit
- Hub Connects Chains: After challenge period, governance connects chains to Hub
- Epoch Operation: Current proposer produces blocks for
epochDurationon ALL chains - Active Handoff: At epoch end, proposer flushes batches and calls
rotateNetwork()(grace period protects this) - Atomic Update: Hub updates each rollup configuration contract on ALL connected chains
- Proposer Agent Activation: New proposer's agent detects the change and immediately starts sequencing
- SLA Enforcement: Misbehaving proposers challenged via the arbitrator (default: Kleros)
The full SLA is defined in policies/policy_sequencer_registry.md.
Key Requirements:
| Requirement | Violation | Evidence Standard |
|---|---|---|
| Authorized Production | Producing blocks when unauthorized | L1 timestamp vs epoch |
| Active Handoff | Missed rotation during grace period | Forced rotation evidence |
| Liveness | >5 minute downtime during epoch | Block production gaps |
Liveness Enforcement: The Hub reports each outgoing proposer's liveness score to the ProposerRegistry during every rotateNetwork() / rotateShard() call. Proposers who rotate within the grace period receive full liveness credit; forced rotations (past grace period) are penalized proportionally. Liveness scores below 95% trigger automatic slashing. Chains may designate a circuit breaker address (e.g., a security council multisig) in their policy that can officially pause sequencing via setPause(). Paused chains immunize the sequencer from TimingViolation slashing. Unjustified pauses can be disputed via ProofType.UnjustifiedPause through Kleros arbitration.
See the Sequencer Policy for detailed evidence standards.
The central nervous system of ISOCHRON - manages atomic rotation across all connected chains.
// Chain configuration for each connected Spoke
struct ChainConfig {
address rollupConfig; // The rollup configuration contract
address adapter; // Adapter for rollup compatibility
bool isActive; // Whether this chain is active
uint256 chainId; // The L2 chain ID
}
// Core state
address public currentProposer; // Current active proposer
uint256 public currentEpoch; // Current epoch number
uint256 public epochDuration; // Rotation interval (default: 1 hour)
uint256 public gracePeriod; // Active Handoff window (default: 10 min)
address public proposerRegistry; // ProposerRegistry contract
// Rotation functions (report outgoing proposer liveness to ProposerRegistry on each rotation)
function rotateNetwork() external; // Atomic rotation of ALL chains
function rotateShard(uint256 shardIndex); // For scaling beyond 400 chains
// Chain management (governance only)
function connectChain(uint256 chainId, address rollupConfig, address adapter);
function disconnectChain(uint256 chainId);
function updateChainConfig(uint256 chainId, address adapter);
// View functions
function getChainCount() external view returns (uint256);
function getChainConfig(uint256 chainId) external view returns (ChainConfig memory);
function isCurrentProposer(address proposer) external view returns (bool);
function timeUntilNextRotation() external view returns (uint256);
function isRotationWindowOpen() external view returns (bool);
// Guardian (emergency controls)
function pause() external;
function emergencyRotate(address newProposer) external;"The Dumb Pipe" - manages DPoS-based proposer selection with liveness focus.
struct ProposerInfo {
uint256 stake; // Own stake
uint256 delegatedStake; // Stake delegated by others
bool isActive; // In the active set
bool isRegistered; // Registered in system
uint256 livenessScore; // 0-10000 (100.00%)
address operationalKey; // Key for block signing
}
// Configuration
uint256 public minimumStake; // Default: 32 ETH
uint256 public maxActiveSetSize; // Default: 100
// Proposer functions
function register(address operationalKey) external payable;
function addStake() external payable;
function withdrawStake(uint256 amount) external;
function updateOperationalKey(address newKey) external;
// Delegation
function delegate(address proposer) external payable;
function undelegate(address proposer, uint256 amount) external;
// Public functions
function rebalance() external; // Swap low-stake active with high-stake inactive
// Selection & liveness (called by Hub during rotation)
function selectNextProposer(uint256 epoch) external view returns (address);
function commitLookahead(uint256 epoch) external; // Snapshot active set for stable selection
function reportLiveness(address proposer, uint256 epoch, uint256 blocksProduced, uint256 blocksExpected) external;
function slashForLiveness(address proposer, uint256 basisPoints) external;
// View functions
function getActiveProposers() external view returns (address[] memory);
function getProposerLookahead() external view returns (address[] memory);
function getTotalStake(address proposer) external view returns (uint256);
function isActiveProposer(address proposer) external view returns (bool);GeneralizedTCR for decentralized chain onboarding to ISOCHRON:
// Chain registration data
struct ChainData {
uint256 chainId; // L2 chain ID
address rollupConfig; // Rollup configuration contract on L1
address adapter; // Rollup adapter
string name; // Human-readable name
string metadataURI; // IPFS URI with additional info
}
// Item status
enum Status {
Absent, // Not in registry
RegistrationRequested, // Pending, in challenge period
Registered, // Registered and eligible
ClearingRequested // Removal pending
}
// Registration functions
function addChain(
uint256 chainId,
address rollupConfig,
address adapter,
string name,
string metadataURI
) external payable returns (bytes32 itemId);
function removeChain(uint256 chainId) external payable;
function challengeRequest(bytes32 itemId, string evidence) external payable;
function executeRequest(bytes32 itemId) external;
// View functions
function isRegistered(uint256 chainId) external view returns (bool);
function getRegisteredChains() external view returns (uint256[] memory);
function getItemByChainId(uint256 chainId) external view returns (Item memory);Helper contract for easy chain integration into ISOCHRON:
// Registration status tracking
enum RegistrationStatus {
NotStarted, // Chain hasn't been registered
Pending, // In challenge period
Registered, // Successfully registered
Connected, // Connected to ISOCHRON Hub
Failed // Registration failed (challenged)
}
// Simple registration
function registerChain(
uint256 chainId,
address rollupConfig,
string name,
string metadataURI
) external payable returns (bytes32 itemId);
// With custom adapter
function registerChainWithAdapter(
uint256 chainId,
address rollupConfig,
address adapter,
string name,
string metadataURI
) external payable returns (bytes32 itemId);
// Finalize after challenge period
function finalizeRegistration(uint256 chainId) external;
// View functions
function getStatus(uint256 chainId) external view returns (RegistrationStatus);
function isReadyForConnection(uint256 chainId) external view returns (bool);
function getChallengeTimeRemaining(uint256 chainId) external view returns (uint256);
function getRequiredDeposit() external view returns (uint256);Hot-swappable adapter interface for rollup compatibility:
interface ISequencerAdapter {
// Version for ratchet upgrade logic (v1.0.0 = 1_000_000)
function version() external view returns (uint256);
function adapterInfo() external view returns (string memory name, string memory description);
// Called via regular call from the hub; returns calldata for hub to execute
function getRotationCalldata(
address _rollupConfig,
bytes calldata _rotationData
) external view returns (bytes[] memory calls);
}Deployed on every connected L2 chain. Acts as the entry point for all atomic bundle operations, ensuring execution outcomes are always recorded on-chain for fraud proof verification:
// Execute a single bundle operation (inner reverts are caught, not propagated)
function executeBundle(
address target, // The actual DApp contract
bytes calldata data, // Calldata for the DApp
bytes32 bundleId // Cross-chain bundle identifier
) external payable;
// Emits: BundleResult(bundleId, target, success, returnData)
// Execute multiple operations for the same bundle on this chain
function executeBundleBatch(
address[] calldata targets,
bytes[] calldata datas,
uint256[] calldata values,
bytes32 bundleId
) external payable;
// Emits: BundleResult(bundleId, targets[0], allSuccess, returnData)
// Query execution result
function getBundleResult(bytes32 bundleId)
external view returns (bool executed, bool success, uint256 blockNumber);Key Design Property: The outer transaction never reverts. Even if the inner DApp call fails, the AtomicBundleExecutor catches the revert and records success=false in the BundleResult event. This makes failures visible to the L1 fraud proof system.
Manages atomic cross-chain bundle commitments with escrow integration:
// Bundle lifecycle
function commitBundle(
bytes32 operationsHash,
uint256[] calldata targetChainIds,
uint256 deadline
) external payable returns (bytes32 bundleId);
function confirmChainExecution(
bytes32 bundleId, uint256 chainId, uint256 blockNumber, bytes calldata proof
) external;
function completeBundle(bytes32 bundleId) external;
function cancelBundle(bytes32 bundleId) external;
function expireBundle(bytes32 bundleId) external;
function reportViolation(bytes32 bundleId, bytes calldata proof, string calldata reason) external;
// View functions
function getBundle(bytes32 bundleId) external view returns (BundleCommitment memory);
function getBundleStatus(bytes32 bundleId) external view returns (BundleStatus);
function getPendingBundleCount() external view returns (uint256);
function getSequencerBundles(address sequencer) external view returns (bytes32[] memory);Holds tips and bonds for cross-chain bundles:
function depositTip(bytes32 bundleId) external payable;
function postBond(bytes32 bundleId) external payable;
function releaseTip(bytes32 bundleId) external; // Only by BundleRegistry
function slashBond(bytes32 bundleId, address reporter) external; // 10% reporter, 90% governance
function refundTip(bytes32 bundleId) external;
function returnBond(bytes32 bundleId) external;Per-chain sovereign policy management:
// Policy declaration (by chain governance or hub governance)
function declarePolicy(
uint256 chainId,
OrderingStrategy ordering,
EnforcementType enforcement,
uint256 maxBlockTime,
uint256 forcedInclusionDeadline,
bool sandwichProtection,
bool backrunOnly,
address customPolicyContract,
bytes calldata policyData
) external;
// Policy declaration with circuit breaker (sovereign security halt)
function declarePolicyWithCircuitBreaker(
uint256 chainId,
OrderingStrategy ordering,
EnforcementType enforcement,
uint256 maxBlockTime,
uint256 forcedInclusionDeadline,
bool sandwichProtection,
bool backrunOnly,
address customPolicyContract,
bytes calldata policyData,
address circuitBreaker // Address authorized to pause sequencing
) external;
// Circuit breaker: pause/unpause sequencing (callable only by circuitBreaker)
// When paused, sequencer is immunized from TimingViolation slashing.
// Unjustified pauses can be disputed via ProofType.UnjustifiedPause (Kleros).
function setPause(uint256 chainId, bool paused) external;
function getChainPauseInfo(uint256 chainId) external view returns (bool isPaused, uint256 pauseTimestamp, uint256 unpauseTimestamp);
function deactivatePolicy(uint256 chainId) external;
function checkCompliance(uint256 chainId, bytes calldata txData) external view returns (ComplianceResult memory);
function getPolicy(uint256 chainId) external view returns (PolicyDeclaration memory);
function getActivePolicyChains() external view returns (uint256[] memory);
// Chain governance
function setChainGovernance(uint256 chainId, address governor) external; // Hub governance onlyTwo-path fraud proof verification with Kleros escalation:
// Challenge submission
function submitChallenge(
address sequencer,
uint256 chainId,
ProofType proofType,
bytes calldata proofData
) external payable returns (bytes32 challengeId);
// Verification
function verifyDeterministicProof(bytes32 challengeId) external;
function escalateToArbitration(bytes32 challengeId) external payable;
function resolveChallenge(bytes32 challengeId) external; // Auto-accept after deadline
// View functions
function getChallenge(bytes32 challengeId) external view returns (Challenge memory);
function getChallengeStatus(bytes32 challengeId) external view returns (ChallengeStatus);
// Governance
function setChallengeBond(uint256 newBond) external;
function setResponseWindow(uint256 newWindow) external;Registry for approved block builders with per-chain overrides:
function registerBuilder(address builder) external; // Governance only
function deactivateBuilder(address builder) external;
function activateBuilder(address builder) external;
function setDefaultBuilder(address builder) external;
function setChainBuilder(uint256 chainId, address builder) external;
function getEffectiveBuilder(uint256 chainId) external view returns (address);
function getActiveBuilders() external view returns (address[] memory);
function getBuilder(address builder) external view returns (BuilderInfo memory);Default MEV-Boost + Flashblocks builder implementation:
function builderType() external view returns (BuilderType); // PrivateMempool
function supportsChain(uint256 chainId) external view returns (bool);
function validateBuildRequest(BuildRequest memory request) external view returns (bool valid, string memory reason);
function relayEndpoint() external view returns (string memory);
function addChainSupport(uint256 chainId) external; // Governance
function removeChainSupport(uint256 chainId) external;
function setRelayEndpoint(string calldata endpoint) external;ArbitrumAdapterV1 - Arbitrum Nitro integration via SequencerInbox:
// Rotation payload: abi.encode(newBatchPoster, oldBatchPoster)
function getRotationCalldata(address sequencerInbox, bytes calldata rotationData) external view returns (bytes[] memory);GenericAdapterV1 - Any EVM rollup via arbitrary calls:
// Single-call: abi.encode(bytes4 selector, bytes callData)
// Multi-call: 0xFF prefix + abi.encode(bytes4[] selectors, bytes[] callDatas)
function getRotationCalldata(address rollupConfig, bytes calldata rotationData) external view returns (bytes[] memory);The OP Stack integration lives in src/poc/opstack, including OpStackAdapterV1 and the ISystemConfig interface for Superchain-compliant SystemConfig contracts.
# Start local devnet
./start.sh local
# Interact with contracts
cast call <HUB> "currentProposer()" --rpc-url http://localhost:8545
cast call <HUB> "isCurrentProposer(address)(bool)" <proposer> --rpc-url http://localhost:8545- Configure Environment
cp .env.sepolia.example .env.sepolia
# Edit with your values- Deploy Contracts
source .env.sepolia
./start.sh sepolia
# Or manually:
# forge script script/DeployRemote.s.sol:DeployRemote --rpc-url $RPC_URL --broadcast --verify- Transfer rollup config ownership (e.g., SystemConfig for OP Stack)
cast send $SYSTEM_CONFIG "transferOwnership(address)" $HUB_ADDRESS \
--rpc-url $L1_RPC \
--private-key $OWNER_PRIVATE_KEY- Register Operators in the arbitrator registry (Kleros Curate by default)
Visit https://curate.kleros.io/ and submit proposer entries as required by your registry schema.
- Deploy ISOCHRON Proposer Agent
Each proposer must run their own agent:
cd agent
pip install -r requirements.txt
cp kssn_config.example.yaml kssn_config.yaml
# Edit kssn_config.yaml with proposer's details
python kssn_proposer_agent.py --config kssn_config.yamlPrerequisites:
- Curate-style TCR deployed with your sequencer policy (Kleros Curate by default)
- TCR item type: tuple (address batcher, address unsafeSigner)
- OP Stack L1 contracts deployed
- Guardian multisig set up
- Keeper infrastructure ready
See script/DeployRemote.s.sol for detailed deployment steps.
Each proposer MUST run a proposer agent. See agent/README.md for:
- Installation instructions
- Configuration options
- Systemd service setup
- Troubleshooting guide
Quick start:
cd agent
pip install -r requirements.txt
cp kssn_config.example.yaml kssn_config.yaml
# Edit kssn_config.yaml with your proposer details
python kssn_proposer_agent.py --config kssn_config.yamlKeepers serve as a liveness fallback (Dead Man's Switch) - they can only force rotation after the grace period expires (Phase 3).
Important: Under normal operation, the current operator initiates rotation during the grace period (Phase 2). Keepers are only needed if an operator fails to rotate.
Web3Function.onRun(async (context) => {
const { userArgs, provider } = context;
const hub = new ethers.Contract(
userArgs.hubAddress,
[
"function epochDuration() view returns (uint256)",
"function gracePeriod() view returns (uint256)",
"function epochStartTime() view returns (uint256)",
"function rotateNetwork()"
],
provider
);
const epochDuration = await hub.epochDuration();
const gracePeriod = await hub.gracePeriod();
const epochStartTime = await hub.epochStartTime();
const now = Math.floor(Date.now() / 1000);
// Keepers can only rotate after epoch + grace period (Phase 3 - Dead Man's Switch)
const deadMansSwitchTime = epochStartTime.add(epochDuration).add(gracePeriod);
if (now <= deadMansSwitchTime) {
const timeLeft = deadMansSwitchTime.sub(now);
return { canExec: false, message: `${timeLeft}s until Dead Man's Switch` };
}
// Phase 3: Force rotation if operator hasn't rotated
return {
canExec: true,
callData: hub.interface.encodeFunctionData("rotateNetwork")
};
});Note: Keepers forcing rotation in Phase 3 may cause L2 re-orgs if the outgoing operator has unflushed batches. This is an acceptable tradeoff for liveness - a stalled chain is worse than a re-org.
- Adapters are called via regular call from the Hub (view functions that return calldata)
- The Hub executes the returned calldata against the rollup config, preserving its role as
msg.sender - This eliminates the delegatecall attack vector: adapters cannot modify Hub storage
- Adapters must be registered in the Adapter Registry (Kleros Curate by default)
- Ratchet versioning prevents rollback attacks (newVersion > currentVersion)
- Hydra defense allows multiple submissions to defeat griefing
- Both
batcherHashandunsafeBlockSignerare set in the same transaction - Prevents "half-rotated" states where batches and P2P blocks have different authorities
- Governance stake can be held separately from operational keys
- Compromise of hot keys doesn't affect stake ownership
- Owner can update operational keys via
setOperationalKeys()
- Reverse mapping (
opIdToItemId) enables O(1) registry verification - Prevents DoS during rotation with many operators
- Snapshots decouple from registry reads during rotation
- SLA requirements enforced via the arbitrator (Kleros default)
- Proposers can be challenged for producing blocks while unauthorized
- All loops are bounded to prevent DoS
- O(1) add/remove using swap-pop pattern
- High deposit requirement in the arbitrator registry (Kleros default) deters frivolous challenges
- Guardian can pause in emergencies
- Hydra defense for adapter submissions
- Only the active sequencer (verified via Hub) can commit bundles
- Bundle tips held in escrow until execution is confirmed on ALL target chains
- Bond slashing deters non-delivery (10% reporter reward incentivizes monitoring)
- Minimum deadline duration prevents unreasonably short execution windows
- Challenge bonds prevent spam (0.5 ETH default)
- Deterministic proofs are trustless - no oracle or committee required
- Auto-accept after deadline prevents censorship of valid challenges
- Kleros arbitration only for inherently subjective criteria (MEV violations)
- Response window (24h default) balances sequencer defense and challenger protection
- Only chain governance (or hub governance) can set chain policies
- Custom policy contracts are isolated - compliance checks via staticcall
- Default policy (SequencerDiscretion) is permissive - chains opt into stricter rules
- Policy deactivation doesn't delete state, allowing re-activation
- Protocol does not require TEEs, threshold encryption, or trusted hardware
- All enforcement is either deterministic (on-chain math) or decentralized (Kleros)
- Chains MAY opt into trusted setups via sovereign policy, but the protocol doesn't mandate them
op/
├── src/
│ ├── SharedSequencerHub.sol # ISOCHRON Hub - atomic multichain rotation
│ ├── ProposerRegistry.sol # DPoS proposer management
│ ├── ChainRegistry.sol # GeneralizedTCR for chain onboarding
│ ├── ChainDeploymentKit.sol # Helper for chain integration
│ │
│ ├── bundle/ # Cross-chain bundle execution
│ │ ├── AtomicBundleExecutor.sol # L2 Spoke executor for optimistic state atomicity
│ │ ├── CrossChainBundleRegistry.sol # Atomic multi-chain bundle commitments
│ │ └── BundleEscrow.sol # Tip/bond escrow with slashing
│ │
│ ├── builder/ # Universal block building
│ │ ├── BuilderRegistry.sol # Pluggable builder management
│ │ └── FlashblocksBuilder.sol # MEV-Boost + Flashblocks (default)
│ │
│ ├── policy/ # Sovereign chain policies
│ │ ├── SovereignPolicyManager.sol # Per-chain policy declaration
│ │ └── DefaultPolicy.sol # Default FCFS + sandwich protection
│ │
│ ├── fraud/ # Fraud proof verification
│ │ └── FraudProofVerifier.sol # Deterministic + Kleros arbitration
│ │
│ ├── poc/ # Chain-specific adapters
│ │ ├── opstack/
│ │ │ ├── OpStackAdapterV1.sol # OP Stack Bedrock/Ecotone adapter
│ │ │ └── interfaces/
│ │ │ └── ISystemConfig.sol # OP Stack SystemConfig interface
│ │ ├── arbitrum/
│ │ │ ├── ArbitrumAdapterV1.sol # Arbitrum Nitro adapter
│ │ │ └── interfaces/
│ │ │ └── IArbitrumRollup.sol # SequencerInbox + RollupCore
│ │ └── generic/
│ │ └── GenericAdapterV1.sol # Any EVM rollup (arbitrary calls)
│ │
│ └── interfaces/
│ ├── ISharedSequencerHub.sol # Hub interface
│ ├── IProposerRegistry.sol # Proposer registry interface
│ ├── IChainRegistry.sol # Chain registry interface
│ ├── ISequencerAdapter.sol # Adapter interface
│ ├── ICrossChainBundle.sol # Bundle registry interface
│ ├── IAtomicBundleExecutor.sol # L2 atomic executor interface
│ ├── IBundleEscrow.sol # Escrow interface
│ ├── IBuilderRegistry.sol # Builder registry interface
│ ├── IUniversalBuilder.sol # Builder interface
│ ├── ISovereignPolicy.sol # Policy interface
│ ├── IFraudProofVerifier.sol # Fraud proof interface
│ ├── ICurate.sol # Kleros Curate interface
│ ├── IArbitrator.sol # ERC-792 arbitration
│ └── IArbitrable.sol # ERC-792 arbitrable
│
├── relay/ # Rust relay (time-sensitive components)
│ ├── Cargo.toml # Rust dependencies
│ └── src/
│ ├── main.rs # Entry point
│ ├── config.rs # TOML configuration
│ ├── bundle/
│ │ ├── types.rs # Bundle/operation types
│ │ ├── validator.rs # Bundle validation
│ │ └── sequencer.rs # Bundle lifecycle
│ ├── chain/
│ │ └── mod.rs # Chain adapter management
│ ├── policy/
│ │ └── mod.rs # Policy compliance engine
│ └── relay/
│ └── mod.rs # HTTP API server
│
├── test/
│ ├── SharedSequencerHub.t.sol # Hub tests (54 tests)
│ ├── ProposerRegistry.t.sol # Proposer registry tests (41 tests)
│ ├── ChainRegistry.t.sol # Chain registry tests (24 tests)
│ ├── CrossChainBundle.t.sol # Bundle + escrow tests (25 tests)
│ ├── BuilderRegistry.t.sol # Builder + Flashblocks tests (19 tests)
│ ├── SovereignPolicy.t.sol # Policy manager tests (18 tests)
│ ├── FraudProofVerifier.t.sol # Fraud proof tests (15 tests)
│ ├── ChainAdapters.t.sol # Arbitrum + Generic adapter tests (12 tests)
│ ├── OpStackAdapterV1.t.sol # OP Stack adapter tests (20 tests)
│ └── mocks/
│ ├── MockProposerRegistry.sol # Proposer registry mock
│ ├── MockChainRegistry.sol # Chain registry mock
│ ├── MockRollupConfig.sol # Generic rollup config mock
│ ├── MockSequencerAdapter.sol # Generic adapter mock
│ ├── MockSystemConfig.sol # OP Stack SystemConfig mock
│ ├── MockAdapterV2.sol # V2 adapter stub
│ ├── MockArbitrator.sol # Kleros arbitrator mock
│ ├── MockHub.sol # Hub mock (for bundle tests)
│ └── MockSequencerInbox.sol # Arbitrum SequencerInbox mock
│
├── script/
│ ├── DeployKSSN.s.sol # Hub-and-Spoke deployment
│ ├── DeployRemote.s.sol # Sepolia/Mainnet deployment
│ ├── IntegrationTest.s.sol # Solidity integration test
│ └── run_integration_test.sh # Full system integration test
├── policies/
│ ├── policy_sequencer_registry.md # Sequencer SLA rules
│ ├── policy_adapter_registry.md # Adapter acceptance criteria
│ └── policy_chain_registry.md # Chain registry criteria
├── devnet/
│ ├── genesis-l2.json # L2 genesis configuration
│ └── generate-configs.sh # Config generation helper
├── docker/
│ └── config/ # Generated L2 configs
├── agent/
│ ├── kssn_proposer_agent.py # Proposer agent
│ ├── kssn_config.example.yaml # Agent config template
│ ├── requirements.txt # Python dependencies
│ └── README.md # Agent documentation
├── docker-compose.yml # Full OP Stack setup
├── start.sh # One-command startup script
├── Makefile # Development commands
└── .env.*.example # Environment templates
Q: What is ISOCHRON? A: ISOCHRON is a Hub-and-Spoke architecture that manages sequencing for multiple rollups from a single Hub contract. It enables atomic cross-chain composability while preserving chain sovereignty through opt-in rotation and SLA governance.
Q: How does atomic multichain rotation work?
A: When rotateNetwork() is called, the Hub iterates through ALL connected chains and updates each rollup configuration contract in a single transaction. This costs ~60k gas per chain, supporting ~450 chains per block at 30M gas limit. For larger networks, use rotateShard().
Q: How is sequencing policy enforced?
A: ISOCHRON enforces SLA expectations (liveness, authorized production, and clean handoffs) through multiple mechanisms: the Hub reports liveness scores to the ProposerRegistry at every epoch rotation, the FraudProofVerifier handles deterministic proofs (timing, ordering, inclusion, bundle violations) and subjective proofs via Kleros arbitration (MEV, custom, unjustified pause), and sovereign chains can set circuit breakers to pause sequencing during security incidents without triggering liveness penalties.
Q: Can the sequencer use Rollup Boost and Flashblocks? A: Yes. FlashblocksBuilder is the default builder in the BuilderRegistry, using a private mempool with MEV-Boost integration. The BuilderRegistry supports per-chain overrides, so individual chains can opt into different building mechanisms (public mempool, encrypted mempool, custom) via their sovereign policy.
Q: How do I connect a new chain to ISOCHRON? A: There are two paths:
Decentralized Path (ChainRegistry):
- Deploy your rollup chain with a configuration contract (e.g., SystemConfig)
- Use ChainDeploymentKit to register in ChainRegistry (GeneralizedTCR)
- Wait for challenge period (community can dispute invalid chains)
- After approval, Hub governance calls
connectChainFromRegistry(chainId) - Your chain is now part of the shared sequencer network
Direct Path (Governance Only):
- Deploy your rollup chain with a configuration contract (e.g., SystemConfig)
- Transfer rollup config ownership to the Hub
- Call
hub.connectChain(chainId, rollupConfig, adapter)as governance - Your chain is now part of the shared sequencer network
Q: What happens if a chain's rotation fails? A: The Hub continues with other chains and deactivates the failing chain. Individual chain failures don't block the network. The chain can be reactivated after fixing the issue.
Q: How do I become a proposer?
A: 1) Call proposerRegistry.register(operationalKey) with minimum stake (32 ETH default), 2) Run the ISOCHRON proposer agent, 3) If your stake is in the top-N (100 by default), you'll be in the active set.
Q: What is the Active Handoff protocol? A: A 3-phase state machine ensuring zero-downtime transitions. During the grace period (10 min default) after epoch ends, only the current proposer can trigger rotation. This allows flushing batches before handover.
Q: What happens during ISOCHRON rotation?
A: 1) Current proposer's agent detects epoch end, 2) Agent stops sequencing and flushes batches, 3) Agent calls rotateNetwork(), 4) Hub updates ALL connected chains atomically, 5) New proposer's agent detects change and starts.
Q: What if the proposer doesn't rotate during grace period?
A: After grace period expires (Phase 3), anyone can force rotation via rotateNetwork(). This ensures liveness but may cause re-orgs if the outgoing proposer had unflushed batches.
Q: How many chains can ISOCHRON support?
A: Single rotateNetwork() supports ~450 chains at 30M gas limit. For larger networks, use rotateShard(shardIndex) which splits rotation into chunks of 200 chains.
Q: What is a "Superchain Aligned" design? A: ISOCHRON is designed to eventually replace the Superchain Council multisig. Instead of a committee updating rollup configuration contracts, the SharedSequencerHub does it algorithmically based on arbitrator governance decisions (Kleros default).
Q: What is a cross-chain bundle?
A: A bundle is an atomic set of operations that must execute across multiple chains with consistent outcomes. Users submit bundles through the relay, which routes each operation through the AtomicBundleExecutor on the target chain. The executor wraps calls so that even if the inner logic reverts, the outcome is recorded on-chain via a BundleResult event. The sequencer commits to the bundle on L1 by providing an operations hash, target chain IDs, and a deadline. If the sequencer executes a bundle with mismatched results across chains (success on one, failure on another), anyone can file an atomicity violation fraud proof and the sequencer is slashed.
Q: What is the difference between economic atomicity and state atomicity?
A: Economic atomicity only slashes the sequencer's bond if a bundle fails — the user may still end up with a "broken leg" position (e.g., bought on Chain A but the sell on Chain B reverted). Optimistic state atomicity (what ISOCHRON implements) goes further: the sequencer is required to simulate bundles off-chain and only include them if ALL operations succeed. If any would fail, the entire bundle is excluded from all chains. The AtomicBundleExecutor + FraudProofVerifier enforce this: mismatched BundleResult events across chains are deterministically provable on L1.
Q: How does bundle escrow work? A: Searchers/users deposit tips for their bundles, and sequencers post bonds as economic guarantees. On successful execution, tips are released to the sequencer. On violation, the bond is slashed - 10% goes to the reporter who identified the violation, and 90% goes to the governance treasury.
Q: What happens if a bundle expires?
A: Anyone can call expireBundle() after the deadline passes. The bundle status changes to Expired and escrowed funds can be returned. Sequencers are expected to either execute or cancel bundles before expiry.
Q: How do sovereign policies work?
A: Each chain can declare its own sequencing policy via SovereignPolicyManager.declarePolicy(). The policy specifies ordering strategy (FCFS, priority fee, sequencer discretion, or custom), enforcement type (deterministic, subjective, or hybrid), timing constraints (max block time, forced inclusion deadline), and MEV protections (sandwich protection, backrun-only). The active sequencer must obey these policies or face fraud proof challenges.
Q: Can a chain use a custom policy contract?
A: Yes. Set orderingStrategy to Custom and provide a customPolicyContract address that implements the ISovereignPolicy interface. The SovereignPolicyManager will delegate compliance checks to that contract.
Q: Do chains need TEEs or other trusted setups? A: No. ISOCHRON does not hardcode any trusted setups. Deterministic fraud proofs handle timing, ordering, and inclusion violations trustlessly on-chain. Subjective criteria (MEV violations) are resolved via Kleros arbitration. However, a chain MAY opt into TEEs or other mechanisms via its sovereign policy and custom policy contract.
Q: What types of fraud can be proven deterministically?
A: Five types: (1) Timing violations - block gap exceeds the chain's maxBlockTime; (2) Ordering violations - FCFS misordering when the policy requires FCFS; (3) Inclusion violations - censorship beyond the forcedInclusionDeadline; (4) Bundle violations - committed bundle deadline passed without execution; (5) Atomicity violations - a cross-chain bundle was executed with mismatched BundleResult outcomes across chains (e.g., success on Chain A, failure on Chain B). The challenger submits Merkle proofs of the BundleResult event logs from both chains.
Q: How does Kleros arbitration work for subjective violations? A: For MEV violations (sandwich attacks, front-running) and custom violations, the challenger escalates to Kleros by paying the arbitration fee. A Kleros court examines the evidence and rules in favor of either the challenger or the sequencer. The bond is then distributed accordingly.
Q: What if nobody responds to a challenge?
A: If a challenge remains in Pending status for longer than the response window (24 hours default), anyone can call resolveChallenge() to auto-accept it. The challenger gets their bond back.
Q: How do I integrate a non-OP-Stack chain? A: Use one of the existing adapters or write a new one:
- Arbitrum Nitro: Use
ArbitrumAdapterV1which callssetIsBatchPoster()on the SequencerInbox - Any EVM rollup: Use
GenericAdapterV1which supports arbitrary function calls (single or multi-call mode) - Custom: Implement the
ISequencerAdapterinterface with your chain's specific rotation logic
Q: Does integrating with ISOCHRON require changes to my rollup? A: No. Adapters are view functions that return calldata for the Hub to execute against your rollup config. Your rollup's existing contracts remain unchanged. You just need to transfer ownership/permissions of the relevant configuration contract (SystemConfig, SequencerInbox, etc.) to the Hub.
Q: Why is there a Rust relay? A: The relay handles time-sensitive components that benefit from Rust's performance and safety guarantees: bundle validation, ordering policy enforcement, and the HTTP API for bundle submission. The Solidity contracts provide the settlement and dispute resolution layer, while the relay handles real-time processing.
Q: How do I run the relay?
A: Build with cd relay && cargo build --release, then configure via config.toml (see relay/src/config.rs for options). The relay starts an HTTP server with endpoints for health checks, bundle submission, and status queries.
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Write tests for new functionality
- Submit a pull request
MIT