Abstract
This paper describes onRails, a technology that enables Ethereum assets to be brought onto the Canton Network.
The protocol implements a lock/mint and burn/release model coordinated by an off‑chain relayer. The relayer is responsible for observing source‑chain events, enforcing finality, preventing replay, and executing the corresponding action on the destination chain.
The design goals are practical operability and correctness in the presence of real‑world failures: node/RPC outages, relayer restarts, chain reorgs, and partial execution. The system explicitly prioritizes deterministic processing, recoverability, and auditability, while keeping on‑chain components minimal and stable.
Design Principles
1. Introduction
1.1 Problem Statement
Moving assets between different chains is a complex task, which often requires usage of centralized exchanges. This complicates user onboarding to the new chains, leads to fragmented liquidity, limits possible cross-chain farming strategies, and leads to less accurate pricing of on-chain assets due to smaller arbitrage options.
1.2 Scope
The protocol bridges ERC-20 between Ethereum and Canton, for example ETH on Ethereum to cETH on Canton. It is centralized by design, with a trusted relayer enforcing limits and correctness, with future plans of making it decentralized.
In-scope:
- EVM deposit ingestion (event scanning and reorg recovery).
- Canton withdrawal ingestion (ledger streaming to relayer).
- Idempotency, retries, and state tracking via a database.
- Operational controls (timeouts, observability, manual recovery tools).
1.3 Prior Work
Bridges traditionally use lock/mint and burn/release patterns with a trusted attestation layer. The presented design follows this paradigm while focusing on deterministic backend processing, explicit checkpoints, and reorg recovery. It also borrows operational patterns from centralized settlement systems: strict state transitions, idempotency, and audit‑friendly logs.
2. Protocol Overview
2.1 Components
| Layer | Stack | Components |
|---|---|---|
| Ethereum | Solidity | BridgeVault — holds ERC-20 deposits and releases on withdrawal.BridgeRouter — user entry point; emits Deposit/Withdraw events, enforces limits and fee policy. |
| Canton | Daml | BridgeRouter — mints/burns ERC-20 tokens when called by the relayer.cETH (CIP‑56) — wrapped asset with canonical token semantics. |
| Backend | Python | EVM watcher, Canton watcher, state store (processed_messages + chain_checkpoints), CLI tools. |
| Database | Postgres | processed_messages — one row per messageId.chain_checkpoints — per watcher checkpoint for resumability and reorg detection. |
2.1.1 processed_messages schema
| Column | Description |
|---|---|
message_id | Unique cross-chain message identifier (0x hex) |
status | DETECTED, PROCESSING, COMPLETED, FAILED |
tx_hash_in | Source chain tx hash (EVM) or contract ID (Canton) |
tx_hash_out | Destination chain tx hash |
block_number | EVM block where deposit was found |
log_index | EVM log index within block |
src_input_token | Source token address |
src_input_amount | Amount in base units |
src_chain_id | Source chain ID |
dst_chain_id | Destination chain ID |
dst_output_token | Destination token identifier |
recipient | Destination address / party |
created_at | Row insertion time |
updated_at | Last status change time |
2.1.2 chain_checkpoints schema
| Column | Description |
|---|---|
chain | Namespace key (chain:router:dst) |
key | Checkpoint type (e.g., deposit_last_processed_block) |
value | Block number or ledger offset |
block_hash | Hash at checkpoint height (reorg detection) |
updated_at | Last update time |
2.2 Message Model
Each transfer is identified by a unique messageId (bytes32). The relayer stores and checks this ID to prevent replay and to allow safe restarts. The messageId is carried end‑to‑end and is the primary correlation key in logs and the database.
The bridge treats a “message” as an immutable intent with an execution lifecycle. The relayer records: source chain identifier and transaction hash, destination chain identifier and transaction hash (once executed), amounts and addresses/parties, and timestamps for observability.
event Deposit(
bytes32 messageId,
address srcInputToken,
uint256 srcInputAmount,
uint256 srcChainID,
uint256 dstChainID,
bytes32 dstOutputToken,
uint256 dstMinOutputAmount,
bytes32 recipient
);event Withdraw(
bytes32 indexed messageId,
address indexed token,
address indexed recipient,
uint256 amount
);template WithdrawEvent
with
messageId : Text -- hex of bytes32
token : Text -- canton address
recipient : Text -- EVM address
amount : Decimal
relayer : Party
auditObservers : [Party]
where
signatory relayer
observer auditObservers2.2.4 Canonical message fields
| Field | Type | Description |
|---|---|---|
messageId | bytes32 | Unique identifier |
srcChainId | uint256 | Source chain ID |
dstChainId | uint256 | Destination chain ID |
token | address / Text | Token address (EVM) or identifier (Canton) |
sender | address / Party | Source address or party |
recipient | Party / address | Destination party or address |
amount | uint256 / Decimal | Amount in base units |
nonce / salt | optional | Uniqueness aid |
metadata | optional | Additional reference data |
2.3 Trust Model
The relayer is trusted to:
- execute mint/burn and withdrawals correctly,
- enforce limits and validation rules,
- respect finality and reorg safety.
Users trust that the relayer will not mint unbacked assets and will not release funds without a valid Canton withdrawal. Operational controls, audit logs, and DB state transitions provide accountability.
In practice this implies: the relayer's key management and operational procedures are part of the security boundary. The bridge is only as reliable as its RPC/data sources unless the relayer cross‑checks providers.
2.4 State Machine
The relayer uses a small set of statuses in processed_messages to drive idempotent processing. The state machine is intentionally simple — it can be extended later with finer granularity if operational requirements demand it.
| Status | Meaning | Typical next step |
|---|---|---|
DETECTED | Source event observed and validated; not yet executed on destination | Execute destination action |
PROCESSING | Destination action submitted or in-flight | Wait receipt / finality or retry |
COMPLETED | Destination action confirmed and finalized | No further action |
FAILED | Terminal failure requiring operator action | Manual retry / fix / cancel |
┌─────────────────────┐
│ DETECTED │
└──────────┬──────────┘
│ submit destination action
▼
┌─────────────────────┐
┌─────────│ PROCESSING │─────────┐
│ └──────────┬──────────┘ │
│ │ │
tx reverted / receipt + finality unrecoverable
timeout/nonce │ error
│ ▼ │
│ ┌─────────────────────┐ │
│ │ COMPLETED │ │
│ └─────────────────────┘ │
└──────────────────┬──────────────────────┘
▼
┌─────────────────────┐
│ FAILED │
└─────────────────────┘3. Protocol Flows
3.1 EVM → Canton (Lock / Mint)
BridgeRouter.deposit on Ethereum.Deposit event is emitted.latest − confirmations).DETECTED.BridgeRouter.mint.DETECTED → PROCESSING → COMPLETED or FAILED.┌───────────────────────────────────────────────────────────┐ │ EVM → Canton (Lock / Mint) │ ├───────────────────────────────────────────────────────────┤ │ User Ethereum Backend │ │ │── deposit() ──►│ │ │ │ │ │── Deposit event ───────►│ │ │ │ │ (wait confirmations) │ │ │ │ │ ┌──────────┴──────────┐ │ │ │ │ │ validate & insert │ │ │ │ │ │ status = DETECTED │ │ │ │ │ └──────────┬──────────┘ │ │ │ │ ▼ │ │ │ │ ┌─────────────────────┐ │ │ │ │ │ Canton JSON API │ │ │ │ │ │ mint on BridgeRouter│ │ │ │ │ └──────────┬──────────┘ │ │ │ │ status = COMPLETED │ └───────────────────────────────────────────────────────────┘
3.1.1 Validation checklist
- Event authenticity — emitted by the configured BridgeRouter address.
- Token correctness — the deposited token is the expected ETH/cETH contract.
- Amount bounds —
amount >= minAmountandamount <= maxPerTx. - Rate limits — daily limit per token and/or per user.
- Destination validity — destination chain and recipient party/address allowed.
3.1.2 Failure modes
- RPC errors during scan — handled via retries; scan is resumable.
- Canton submission fails — message remains
DETECTEDor transitions toFAILED. - Relayer crash after submission — recovered via DB state; retries are safe and idempotent.
3.2 Canton → EVM (Burn / Release)
DepositEvent with message data.DETECTED, then immediately PROCESSING.finalizeWithdraw via EVMSigner.EVM_TX_CONFIRMATIONS blocks).COMPLETED or FAILED.3.2.1 Failure modes
- Canton stream interruption — resumable from checkpoint; backlog catch‑up.
- Ethereum tx underpriced / stuck — processing monitor alerts; operator can replace tx.
- Ethereum reorg before finality — relayer continues waiting or re-submits as needed.
3.3 Exactly-Once vs At-Least-Once
The relayer processes external events at-least-once (events can be re-observed after restart or rollback), but the combined design aims for exactly-once effects by enforcing idempotency at the message level: source observation can repeat; destination execution is guarded by messageId uniqueness and DB state.
The system provides “exactly-once per messageId” semantics, assuming correct DB operation and consistent messageId derivation.
4. State, Finality, and Reorgs
4.1 Checkpointing
chain_checkpoints stores the last fully scanned block and its block hash. On restart, the watcher resumes from this checkpoint. Checkpoints are namespaced by chain ID, router address, and destination chain to prevent collisions across networks.
- Progress — resume scanning without rescanning from genesis.
- Integrity — detect when a previously processed block has changed due to reorg.
4.2 Reorg Detection and Recovery
The watcher compares the stored checkpoint hash with the current chain hash at the same height. A mismatch triggers rollback by a safety buffer, deletes affected rows, and rescans the range.
The rollback strategy is conservative: roll back before the divergence by a fixed buffer; delete any processed_messages above the rollback height; rescan deterministically from the rollback height to the safe head.
This approach intentionally prefers correctness over speed.
4.3 Idempotency
processed_messages ensures each messageId is processed once, supporting safe retries and relayer restarts. The DB is the source of truth for operational state and auditing.
The relayer always queries/updates by messageId and treats duplicates as no‑ops.
4.4 Finality Policy
The relayer separates inclusion (tx mined, has receipt) from finality (tx reached a configured confirmation depth).
For EVM deposits, the watcher only scans up to latest − confirmations. For EVM withdrawals, the signer waits for the receipt and then waits until the receipt's block has enough confirmations.
5. Reliability Controls
- Confirmations — avoid processing near‑head blocks.
- Exit checkpointing — resumable processing on shutdown.
- Catch‑up mode — skips waiting time to reach latest block quickly.
5.1 Configuration Parameters
Operators should treat the following as explicit risk knobs. Tune per chain and provider characteristics.
| Parameter | Purpose | Risk if too low | Risk if too high |
|---|---|---|---|
confirmations | Reorg safety depth | Process reorged events | Increased latency |
rollback_buffer | How far to roll back on reorg | Miss divergence edges | Slower recovery |
max_chunk_size | eth_getLogs chunking | RPC failures / timeouts | More RPC calls |
processing_timeout | Alert threshold for stuck messages | Alert fatigue | Slow detection |
6. Security Considerations
- The relayer is a single point of control; access and key management are critical.
- RPC integrity is required; using multiple providers is recommended.
- Database integrity is required for auditability and recovery.
- Operational access should be limited, monitored, and rotated.
6.1 Threat Model (Centralized)
This system should be analyzed as a centralized custody/settlement component: a compromised relayer key can steal funds; a compromised RPC provider can cause incorrect observations or delayed processing; a compromised database can hide or distort operational history.
6.1.1 Attack Surface Summary
| Component | Asset at risk | Attack vector | Impact |
|---|---|---|---|
Relayer EOA key | Vault funds | Key theft | Total loss — limited by daily contract caps |
RPC provider | Correct observations | MITM / eclipse | Double-spend — prevented by contract validation |
Database | Audit trail | SQL injection / credential leak | Altered history — rescannable |
Canton JSON API | Mint / burn | Token hijack / unauthorized exercise | Unbacked mint |
Process / host | All | RCE / container escape | Full compromise |
6.2 Practical Mitigations
- Hardware-backed key storage and strict operational procedures.
- Multiple independent RPC providers; compare results at critical boundaries.
- Strict allow‑lists and rate limits on all bridge inputs.
- Run relayer in isolated network environment.
- On-chain validation which prohibits transaction replay.