How does a transaction sent to Ethereum become part of the Facet chain? This document explains both the high-level process and detailed technical implementation. For a one-page diagram, see the Architecture Overview.
Overview
When you send a Facet transaction to Ethereum's 0xface7 address, Facet nodes automatically detect it and include it in the Facet block for the corresponding 12-second slot. Facet produces blocks every 12 seconds regardless of whether Ethereum produced an L1 block in that slot. If L1 produced a block, the slot’s Facet block includes all 0xface7 inbox transactions from that L1 block in exact order. If L1 skipped the slot (no L1 block), Facet still produces a filler block with no inbox transactions. This is a deterministic process—no sequencer or admin can interfere.
The system uses two components: facet-node monitors L1 and builds blocks, while facet-geth executes transactions and maintains state. This architecture ensures that anyone running these components will derive exactly the same chain state from the same Ethereum data.
Transaction Processing Pipeline
1. L1 Block Monitoring
facet-node monitors L1 blocks using these RPC calls:
eth_getBlockReceipts: Retrieves logs and transaction status
2. Facet Transaction Detection
Valid Facet transactions are identified by:
Calldata transactions: to address equals address(0xface7) with receipt status = 1
Event log transactions: Exactly one topic equal to bytes32(uint256(0xface7)) with payload in data field
One Transaction Per Ethereum Transaction Rule:
Each Ethereum transaction contributes at most ONE Facet transaction to the L2 block
If multiple valid Facet payloads exist (e.g., calldata + event, or multiple events), only the first is processed
Processing order: Calldata is checked first, then events in log index order
Additional candidates are silently ignored (not an error condition)
3. Transaction Decoding
Facet transactions must decode to this structure:
Deposit Transaction Construction
Type 0x7D Transactions
Facet executes Facet payloads as deposit transactions of type 0x7D (like Optimism’s 0x7E deposit tx, with Facet-specific fields/semantics). The effective L2 transaction has the following data model:
Transaction hash computation:
The typed-transaction hash uses type 0x7D and standard serialization of the above fields.
Important: The Mint field is excluded from the hash by treating it as zero/nil for hashing purposes. In other words, changes to Mint do not affect the transaction hash.
Address Aliasing
For log-originated transactions, the from address is aliased:
This prevents contracts from impersonating EOAs.
Block Construction
L1 Attributes Transaction
Every Facet block begins with an L1 attributes transaction containing:
L1 block number
L1 block timestamp
L1 block hash
Sequence number
Block Timing Rules
Facet block times are fixed at 12-second intervals
When an L1 block exists for a slot, its inbox transactions appear in that slot’s Facet block
When a slot has no L1 block, Facet produces a filler block for that slot (no inbox transactions)
Engine API Communication
Blocks are sent to facet-geth using:
engine_forkchoiceUpdatedV2
engine_newPayloadV2
engine_getPayloadV2
Gas Mechanics
Sequential Gas Buying
Unlike standard EVM chains:
Each transaction attempts to buy gas sequentially
If insufficient gas remains in block, transaction fails
Next transaction attempts to buy gas
Block gas limit is not pre-validated
FCT Minting Rules
For contract-initiated transactions:
Contract cannot increase its own FCT balance
Excess minted FCT transfers to L1 transaction origin
Prevents contracts from self-funding
Base Fee Only
No priority fee mechanism
All transactions pay current base fee
Simplifies gas pricing model
State Transition Differences
Invalid Block Handling
facet-geth modifications ensure:
Invalid blocks cannot be submitted by facet-node
Pre-check errors affect individual transactions, not entire block
// Pseudocode: DepositTx
SourceHash: bytes32 // The tx hash of the L1 transaction that originated the Facet payload
From: address // For calldata-originated payloads, this is the L1 EOA that signed the envelope; for log-originated payloads, this is the emitting contract address after aliasing (see below)
To: address? // Destination address; `nil` for contract creation
Mint: uint256? // FCT minted on L2 and locked on L1; nil if none
Value: uint256 // FCT transferred from L2 balance (applied after Mint)
Gas: uint64 // execution gas limit
IsSystemTransaction: bool // Legacy field, always `false`
Data: bytes // calldata