The core reads the registry through the contract, never from raw storage slots, so the registry's internal layout stays free to change.
How a blockchain checks another blockchain
Start here for the idea in plain terms. Keep reading for the mechanism, in full detail, the way an engineer would want it.
The problem with the middle
Two blockchains cannot talk to each other. They are separate systems that only trust their own record. So the industry built bridges: a contract holding deposits on one side, another on the other, and some group of signers deciding when to move value between them.
That group is the weak point. It always has been. Compromise the signers or their code and the money in the middle is gone.
What we did instead
L1X validators, the machines that produce blocks, also watch other blockchains directly.
When something happens on another chain, each validator looks at it themselves and waits until it is deep enough in that chain's history to be permanent. When a block is proposed containing that event, every other validator compares it against what they saw with their own eyes. If it does not match exactly, they refuse to sign. Without their signatures the block does not exist.
So a false event cannot get in, because getting it in would require the honest majority of the network to lie about something they can each independently check.
That is the whole idea. The rest of this page is how it is built.
Three planes
L1X separates the work into three planes, each using the right interface for the job.
Applications register which foreign events they care about. The registry stores it; the core reads it back through the contract.
Verified foreign events are delivered to applications as a consensus gated transaction, written by the core into the Portal.
Applications send messages to other chains. The Portal emits, the core reads them from the block.
If a node could write a foreign event straight into state, one dishonest node could invent one. Making it a transaction forces the whole validator set to agree first.
Control plane: registering interest
An application calls registerListener(chainId, contract, eventSignature, handler) on the Portal. Both are Solidity, so this is an ordinary contract call.
The registration lands in normal L1X state. During block execution the core notices the Portal's state changed, reads the current watch list back through the contract, and refreshes an in memory cache. Watchers start or stop according to the new list.
The refresh is conditional, happening only in blocks where the registry actually changed.
Inbound plane: attested delivery
Each validator runs a watcher per supported chain, holding each event until it is at or beyond that chain's configured finality depth. Watchers never scan to the head of a chain, because a reorg would rewrite what they saw.
The block proposer packages newly verified events as attestation transactions. Caps apply per block and per source so one busy contract cannot crowd out the chain.
Every other validator re checks each attestation against its own watcher cache. The event must exist, match byte for byte, and be at depth. On any mismatch the validator errors out, never signs, and the block cannot reach quorum.
On execution the core calls recordAndExecute on the Portal, which stores the event and invokes the registered handler under a gas cap. If the handler reverts or runs out of gas, the event is recorded as claimable and anyone can retry it with the original payload.
Determinism
Every honest validator must reach the same answer, or blocks fail to finalise for innocent reasons. So:
Never scan to chain head. Only at or beyond the configured finality depth.
Match event bytes exactly. No normalising, no re encoding.
No dependence on wall clock time or local RPC latency.
Rotate across multiple providers, and treat disagreement between them as “not verified” rather than picking a winner.
Finality depths are set per chain and identical across all validators.
Outbound plane: sending messages
An application calls sendMessage(destChainId, destAddress, payload). The Portal emits a canonical event, the core reads block events and hands the message to the signer, validators sign, and a verifier contract on the destination chain checks those signatures against the published L1X validator set before delivering.
Outbound is the direction that requires the destination chain to authenticate L1X, which is why the epoch validator set is published on chain and committed by the protocol rather than configured by an operator.
The Portal contract
A single Solidity contract at a fixed, well known address.
| Function | Caller | Purpose |
|---|---|---|
| registerListener(chainId, contract, eventSig, handler) | Any application | Add a source to the watch list |
| readEvent(messageId) | Any contract, view | Read a verified foreign event |
| sendMessage(destChainId, destAddress, payload) | Any application | Emit an outbound message |
| claim(messageId, payload, gasCap) | Anyone | Retry a delivery whose handler failed |
| recordAndExecute(...) | Protocol only | Store an attested event and invoke the handler |
If any externally controlled key could call recordAndExecute, the trust model would collapse back to a relayer with a private key, which is precisely what this design removes. Events are stored once, keyed by an identity derived from the source chain, contract, transaction hash and log index, and are replay protected.
What this replaces
| Traditional bridge | L1X |
|---|---|
| A contract holding pooled deposits on each chain | No pooled custody in the middle |
| A relayer or committee with signing keys | The validator set that already secures the chain |
| Trust in an external multisig | Trust in L1X consensus |
| Integration code deployed on every chain | Nothing deployed on the source chain |
| Wrapped assets issued by a bridge operator | Assets issued by application contracts on verified events |
Security posture
Every validator re derives its own answer. Agreement is the product of independent observation, not of trusting the proposer.
Events are only usable once they are deep enough on the source chain to be irreversible under normal conditions.
Each event has one identity and is recorded once.
Handlers run under a gas cap; a failing handler cannot stall the chain and its event stays claimable.
The core and cross chain code have been audited by Hashlock, with reports published.
The node, the Portal and the tooling are public. Everything on this page can be read in the source.
Read the papers
Two documents go deeper than any web page should.