# Vea

Vea is a cross-chain message bridge which enables fast and secure interoperability specifically designed with optimistic rollups in mind. Vea specifically solves an unmet need of bridging authenticated data, which no other 3rd party bridge supports.

## Without Vea

Currently the only available options for cross-chain interoperability with optimistic rollups fall into two categories:

* Slow and secure canonical bridge which can take at least 7 days in the case of Optimistic Rollups, or
* Fast and insecure third party bridges taking just between 10 minutes to 1 hour but adding many trust assumptions.

Fast 3rd party bridges today work well for unauthenticated communication. If Alice (chain A) wants to send Bob (chain B) ETH, she can use a liquidity network where a liquidity provider, Charlie, immediately sends Bob (chain B) ETH to speed up the process and is later compensated with ETH locked up by Alice.

With authenticated communication though, no 3rd party can "supply liquidity" fast, for example, NFT transfers. If Alice wants to send Bob an NFT, no liquidity provider can send Bob the same NFT on Alice's behalf, because the NFT is non-fungible, only one exists. More generally, oracle queries are another example of authenticated communication where the result of an oracle cannot be provided by a 'liquidity provider' because the data is unique and the data provider needs to attest to the authenticity of the data.

## With Vea

Vea is a fast and secure bridging protocol which uses a hybrid strategy where a message is optimistically verified.

Vea enables cross-chain authenticated data transfer with faster latency than native bridges. A smart contract on chain A can query an oracle on chain B and receive the oracle response cheaper and sometimes faster than using native bridges. This opens a new possibility of high volume, low-value cross-chain interoperability (cross-chain oracle queries, NFT transfer, governance).

### The Happy Path

The messages are optimistically verified. The bridging speed can take anywhere from hours to a couple of days, depending on the source and destination chain.&#x20;

### The Unhappy Path

The message is sent over the canonical bridge, which can take 7-8 days when sent from optimistic rollups.

### Protocol Design

There is a fundamental trade-off between latency and security: a protocol can be fast & insecure or slow & secure. However, a protocol can be fast & secure as long as the fast mechanism falls back on a slow secure system. Vea is a cross-chain optimistic game that passes messages fast, but always falls back on secure, slow native bridges in case of disputes. This means that Vea is usually fast, but could sometimes slow down.

### Security

Vea is a 1-of-N security model, meaning only 1 honest participant can force the correct bridge execution. More importantly, Vea is a permissionless protocol, meaning you can make sure you are the one honest participant in the protocol.

The contracts are immutable, with no governance nor multisig upgradability risk.

### Interoperability

Vea can connect any L1, L2, L3... etc which settle to Ethereum, including ZK rollups. However, when interoperating with optimistic rollups, there are no added trust assumptions since optimistic rollups use the same trust model as Vea. For this reason, Vea is particularly well suited for optimistic rollup interoperability. Vea can even pass messages between ZK rollups and optimistic rollups without any extra trust assumptions, because to interoperate with an optimistic rollup already accepts a 1 of N security assumption for the optimistic rollup state verification.

## Learning more about Vea

Read on to further explore the technical deep-dive of the Vea protocol and how to get started building cross-chain dapps with Vea.

## Video Explainers

### Vea Overview with Shotaro - Eth Taipei 2023

{% embed url="<https://www.youtube.com/watch?v=Pz8MitQoZAs>" %}
Vea bridge: A permissionless, immutable optimistic bridging primitive | Kleros | ETHTaipei 2023
{% endembed %}


# FAQ

## Why the name?

Vea is latin for a way, a road, a passage. :bridge\_at\_night: We think it's fitting for a cross-chain interoperability solution.

## Why another bridge?

Vea is a lean solution specialized for authenticated message bridging with minimum sacrifice on security. This approach differs from most solutions designed for token bridges and relying on complex routing middleware.

## Wait, aren't other bridges more battle-tested?

If existing bridges are battle-tested, they may as have lost the battle: Nomad, Wormhole, Ronin, BNB, etc. That’s 5 out of the top 6 biggest hacks in crypto (excluding SBF). The design of Vea removes a lot of attack surface which exist with other bridges: no routing middleware, no governance, no multisig, and no upgradability.

![](https://i.imgur.com/XxVOziF.png)

## What type of bridge is this?

A trust-minimized optimistically-verified bridge, open to any participant to fulfill the roles of Oracle, Challenger, or Relayer. The trust model requires only 1 live honest verifier, similar to optimistic rollups.

## What are the advantages of Vea?

<figure><img src="/files/7juUEm2CydLEhyJ2TsBC" alt=""><figcaption><p>$ - Vea is cheaper 🏃 - Vea is faster      than native bridges.</p></figcaption></figure>

Vea is faster and cheaper when bridging from Optimistic rollups to Ethereum consensus L1s like Ethereum mainnet and Gnosis Chain. When bridging to L2s from side-chains, Vea is not faster but much cheaper than native bridges. You can bridge from Gnosis to Arbitrum without directly interacting with Ethereum mainnet, meaning Vea provides a lower cost but slower bridging speed.

## How is this secure?

As an optimistic bridge, it is cheap and fast to use in the happy case, where an Oracle makes an unchallenged claim.

While in the unhappy case, it is no different than using the canonical bridges operated by a particular rollup or side-chain.

There is no need for any additional trust assumption on say a 3rd-party oracle or some slow governance mechanism or trusted DAO multisig to ensure that the message is relayed correctly.

Vea works as long as there is one honest participant, and since Vea is permissionless, you can be that honest participant.

## Is there any limitation? What's the trick?

Vea's design choices do come with some limitations and may not be suitable for every use case. Firstly the focus is on message passing primarily (as opposed to tokens). And secondly, the use case must tolerate the worst-case latency when a slow bridge is involved (egress messages from optimistic rollups typically) which is 7 days, plus potentially 1 extra day for forced inclusion in the most extreme case.

## Is it safe to use a shorter challenge period than Optimistic Rollups (1 week)?

Many optimistic rollups were designed and deployed at a time when Ethereum had a proof-of-work consensus model, as a result, many decisions were made considering the uncertain finality guarantees of a proof-of-work consensus model. Under proof-of-stake, the settlement layer has strong finality guarantees. As a result, chain re-organizations are much more rare. The relevance for optimistic systems involves the risk that transactions including honest challenges to fraudulent claims could be censored by malicious actors attacking the settlement layer causing block reorganizations.

The 1-week delay gives honest parties enough time to participate in an interactive fraud-proving scheme by staking on the correct state or by challenging a state transition in a multistep interactive game. Even if this mechanism was to follow an unhappy path, it must eventually converge to a state where the L2 transaction is included, because it has already been written to L1.

The Arbitrum documentation puts it [this way](https://developer.offchainlabs.com/arbos/l2-to-l1-messaging#protocol-design-details):

> The moment a transaction is published on-chain, any observer can anticipate its result; however, for Ethereum itself to accept its result, the protocol must give time for Arbitrum validators to detect and prove fault if need be.

Kevin Fitcher [summarized](https://kelvinfichter.com/pages/thoughts/challenge-periods/) some of the key decisions in designing the week-long challenge period. The 1-week delay is extremely conservative and is designed around a threat model where an attacker might be trying to steal everything of value on a rollup by issuing a single malicious state update and then censoring the honest parties on a proof-of-work settlement layer.

<figure><img src="/files/dkTYq9NhQEijMslQEXR0" alt=""><figcaption><p>L2 Rollup native bridge threat model</p></figcaption></figure>

In short, Vea is not a token bridge. Vea is designed specifically for arbitrary messages and authenticated data. Unlike the native bridges, there is no $1 billion honey pot to 'win' by attacking Vea. Vea is designed for low-value high-volume dapp use cases for querying cross-chain information (oracle outputs, governance votes, nft ownership, etc).

Vea's launch partner, Kleros, a decentralized subjective oracle, demonstrates the different threat models using Vea.&#x20;

Consider an example where smart contracts on a chain like Gnosis escrow tokens between Gnosis chain participants. When there is a dispute between the escrow parties, they query an oracle, Kleros, that lives on a different chain. This type of oracle query is an unauthenticated call and any cross-chain relayer network like Gelato could fulfill the cross-chain query.

<figure><img src="/files/zmM3nLXl2LAKvSDe8MFH" alt=""><figcaption><p>Smart contracts on Gnosis chain query an oracle living on an Optimistic Rollup</p></figcaption></figure>

When the oracle returns its result, the authenticated data can be sent with Vea. Note that Vea is the only 3rd party bridge designed for sending authenticated data. Other 3rd party bridges are specialized in token transfer and unauthenticated calls (relayer networks) and are not capable of sending authenticated data.

<figure><img src="/files/2dpXbbkUQrCK5yAUoE6E" alt=""><figcaption><p>Cross-chain oracle returns the result of a query through Vea.</p></figcaption></figure>

Dapps integrating Vea escrow tokens typically of value much less than a billion USD and usually between predetermined parties with fixed outcomes. The end users of these protocols tend to be uncorrelated, and the escrowed token amounts are ephemeral. The tokens are only escrowed for the duration of the oracle query, upon receiving the result, the escrowed tokens are released, unlike native L2 bridges which indefinitely escrow billions of assets where a single fraudulent state update can drain all funds to an arbitrary attacker's address.

<figure><img src="/files/I5xJa2eIGYvLqZIDdyNz" alt=""><figcaption><p>Vea threat model: A relatively 'small' application specific honeypot (much less than $10 billion).</p></figcaption></figure>

In Vea, the value an attacker can gain is specific to the contracts in which the attacker is personally escrowing tokens. There's no common honeypot pool of tokens growing indefinitely. This is the problem native rollup bridges face: the honeypot grows until it's profitable for an attacker to launch a network-level attack on the settlement layer. With different Vea deployments, we can calibrate the challenge period to better reflect the consensus model of the settlement layer, and the security needed by individual applications that are using it - for most applications the challenge periods in Vea will be adequately secure.

## Do you support token bridging?

No. Vea is an arbitrary message bridge. Token bridges could be built on top, but Vea is first and foremost a message bridge.

## What are the next milestones?

<figure><img src="/files/9m25Z0fwg50vXEiPpLvz" alt=""><figcaption></figcaption></figure>

Currently, we have a permissioned devnet providing developers a good integration experience testing Vea without the latency which will be experienced in the full testnet or mainnet deployments.

The next milestone is the full permissionless testnet, for which we invite as many people as possible to run watcher nodes.

## Who is building this and why?

The [Kleros Cooperative](https://kleros.io/coop), steward of the [Kleros arbitration protocol](https://kleros.io/), is funding the development of Vea as a strategic solution towards [a cross-chain chain arbitration protocol](https://blog.kleros.io/towards-kleros-v2/) (as opposed to a multi-chain one). After surveying the existing bridging solutions, none of them satisfied the team's security appetite.

The original idea for Vea has been conceived in early 2021 as an internal solution for Kleros and mentioned in the [Kleros Yellow Paper (page 6, section 4.2)](https://kleros.io/yellowpaper.pdf) by Clément Lesaege, William George, and Federico Ast.

During development, it emerged that the bridge could easily be application-agnostic and used by other projects without much extra effort. The absence of both privileged functions and reliance on a centralized party has made this easier.

## How does Vea make money?

Neither Vea as a protocol nor as an organization makes money. Vea is deployed as a public good for anyone to use, and for no one to own.

## Who are the protocol participants?

The oracle role is fulfilled by anyone who observes that a snapshot of the state of messages in the bridge is ready for bridging on the sending chain and submits a claim about that state on the receiver chain.

The challenger role is fulfilled by anyone who does not agree with the claim. The challenger does not need to provide an alternative claim, only to signal its disagreement.

The relayer role is fulfilled by anyone who relays the messages encoded in the verified claims on the receiving chain.

## Why would anybody bother acting a protocol participant?

In the unhappy path of the optimistic mechanism, there is a financial incentive for anyone to submit a challenge and be rewarded with the malicious oracle's deposit.

In the happy path, there is no direct financial incentive for anyone. The participants even spend their own funds for gas. So the primary incentive is the utility derived from getting the message across the bridge. In practice, any user, protocol, or entity whose message is part of an epoch batch has an incentive to participate in the bridging process (whether honestly or not).

## I looked at your code, ever heard of DRY?

It is Kleros' smart contract development philosophy of *generally* not using inheritance. DRY makes the most sense in traditional programming in an unconstrained environment.

Smart contracts are under stronger constraints of security and gas usage, making **the KISS principle more important than the DRY principle**.

![KISS](https://i.imgur.com/YpCuxSq.png)

DRY with smart contracts encourages layers of abstraction (to reuse code) which leads to obfuscation (harder to audit) and higher gas cost (more function calls).

To illustrate this point, Vyper, a newer language made specifically for smart contracts, [does not allow inheritance at all](https://vyper.readthedocs.io/en/stable/index.html?highlight=inheritance#principles-and-goals):

> "Class inheritance requires people to jump between multiple files to understand what a program is doing, and requires people to understand the rules of precedence in case of conflicts (“Which class’s function X is the one that’s actually used?”). Hence, it makes code too complicated to understand which negatively impacts auditability."

## Has the code been audited?

Vea is still under development, an audit may come later.

## Has the protocol been formally verified?

Touché, it has not. If you would like to contribute on this front, please reach out to us!


# Deployment Addresses

The Vea devnet is intended for developers testing smart contract integrations with Vea bridge. The devnet is permissioned and operated by the [Kleros Cooperative](https://kleros.io/coop/) with the address: `0x77101b268523cef7983F90Be7a9234A1aEEF752a`.

### Arbitrum Sepolia -> Chiado Vea Devnet&#x20;

<table><thead><tr><th width="213">Contract</th><th>Address</th></tr></thead><tbody><tr><td><p>VeaInbox</p><p>ArbToGnosis</p></td><td><a href="https://sepolia.arbiscan.io/address/0x2E973e20B24088bc74755a7A5cd1A37Dcb53E061">0x2E973e20B24088bc74755a7A5cd1A37Dcb53E061</a></td></tr><tr><td><p>Router</p><p>ArbToGnosis</p></td><td><a href="https://sepolia.etherscan.io/address/0xfA08cfe2530c01045D953f824b836f6757670cD0">0xfA08cfe2530c01045D953f824b836f6757670cD0</a></td></tr><tr><td><p>VeaOutbox</p><p>ArbToGnosisDevnet</p></td><td><a href="https://gnosis-chiado.blockscout.com/address/0x879A9F4476D4445A1deCf40175a700C4c829824D">0x879A9F4476D4445A1deCf40175a700C4c829824D</a></td></tr></tbody></table>

### Arbitrum Sepolia -> Sepolia Devnet

<table><thead><tr><th width="210">Contract</th><th>Address</th></tr></thead><tbody><tr><td><p>VeaInbox</p><p>ArbToEth</p></td><td><a href="https://sepolia.arbiscan.io/address/0x45138BC4E364A16919C4571699171d774A7590BD">0x45138BC4E364A16919C4571699171d774A7590BD</a></td></tr><tr><td><p>VeaOutbox</p><p>ArbToEthDevnet</p></td><td><a href="https://sepolia.etherscan.io/address/0x60af9Fc1dd7d5bce69a66A8AEf456952b03A39C7">0x60af9Fc1dd7d5bce69a66A8AEf456952b03A39C7</a></td></tr></tbody></table>

### Arbitrum Sepolia -> Chiado Vea Testnet&#x20;

<table><thead><tr><th width="213">Contract</th><th>Address</th></tr></thead><tbody><tr><td><p>VeaInbox</p><p>ArbToGnosis</p></td><td><a href="https://sepolia.arbiscan.io/address/0x162f826E18380567CE0548395a3Ad2A54EA87B96">0x162f826E18380567CE0548395a3Ad2A54EA87B96</a></td></tr><tr><td><p>Router</p><p>ArbToGnosis</p></td><td><a href="https://sepolia.etherscan.io/address/0xd7C54E4cA686a8C51D44534AC6b756676961Fccf">0xd7C54E4cA686a8C51D44534AC6b756676961Fccf</a></td></tr><tr><td><p>VeaOutbox</p><p>ArbToGnosisDevnet</p></td><td><a href="https://gnosis-chiado.blockscout.com/address/0x15aC29269b044E1d9042F597513B27Ffa4A7f257">0x15aC29269b044E1d9042F597513B27Ffa4A7f257</a></td></tr></tbody></table>

### Arbitrum Sepolia -> Sepolia Testnet

<table><thead><tr><th width="210">Contract</th><th>Address</th></tr></thead><tbody><tr><td><p>VeaInbox</p><p>ArbToEth</p></td><td><a href="https://sepolia.arbiscan.io/address/0x8B925669606026CcCfAFD72840F5b0CAeDA80078">0x8B925669606026CcCfAFD72840F5b0CAeDA80078</a></td></tr><tr><td><p>VeaOutbox</p><p>ArbToEthDevnet</p></td><td><a href="https://sepolia.etherscan.io/address/0xf720FA4575FB2FE96c7f05B1b5abc2d281cDa09a">0xf720FA4575FB2FE96c7f05B1b5abc2d281cDa09a</a></td></tr></tbody></table>


# Technical Deep Dive

## Contracts

For each sending and receiving chain pair, there is a separate set of Vea contract deployments. For each chain, there is exactly 1 deployed contract.

* Sending Chain: [VeaInbox](https://github.com/kleros/vea/blob/571a1ebbf897fdc29790f4737b70661f292b7aa5/contracts/src/arbitrumToEth/VeaInboxArbToEth.sol) - Manages the state of all messages sent through Vea.
* Intermediary Chain(s): [Router](https://github.com/kleros/vea/blob/2410617e6e6c243bc3108059c39703350031ead2/contracts/src/arbitrumToGnosis/RouterArbToGnosis.sol) - Routes native bridge messages
* Receiving Chain: [VeaOutbox](https://github.com/kleros/vea/blob/571a1ebbf897fdc29790f4737b70661f292b7aa5/contracts/src/arbitrumToEth/VeaOutboxArbToEth.sol) - Manages optimistic game over inbox state

## Epochs

<figure><img src="/files/UQlu420KOqMwOyyclKoW" alt=""><figcaption></figcaption></figure>

Time is partitioned into epochs defined by an epochPeriod. Epochs mark the period between potential bridging events. In other words, epochPeriod defines the highest frequency of bridge operation. On the devnet, we use 30 min. On the full, permissionless testnet and on mainnet we will likely use a period of several hours.

## Merkle Trees

### Motivation

Merkle trees are a common tool for data storage. All messages sent through Vea are inserted into an append-only merkle tree maintained in the vea inbox contract on the sending chain.

An optimistic mechanism transfers the root of the tree to vea outbox contract on the receiving chain, then messages can be relayed by proving inclusion in the merkle tree represented by the root (merkle proofs). These proofs are a logarithmic size of the number of messages in the tree.

### Merkle Mountain Range

<figure><img src="/files/h9JAg85ml6GUBmmSBTD0" alt=""><figcaption><p>Merkle Mountain range representing 7 messages</p></figcaption></figure>

The type of merkle tree implemented in the Vea contracts is sometimes referred to as a merkle mountain range ([MMR](https://github.com/mimblewimble/grin/blob/master/doc/mmr.md)). Without additional context, merkle trees are usually understood to be perfectly balanced binary trees with a fixed height. MMRs on the other hand grow in height as more leafs are inserted, and can be represented by a set of merkle subtrees, referred to as merkle "mountain ranges" due to their shape, see diagram above. For more, see the [implementation details](/introduction/technical-deep-dive/implementation-details).

## Optimistic Bridging

### 0. Send a Message (Sending Chain)

In the VeaInbox, messages are sent through Vea by calling&#x20;

```
    function sendMessage(address _to, bytes memory _data) 
```

This [function](https://github.com/kleros/vea/blob/571a1ebbf897fdc29790f4737b70661f292b7aa5/contracts/src/arbitrumToEth/VeaInboxArbToEth.sol#L75) encodes the message to send including the msg.sender of the call, and inserts a hash of the message encoding into the merkle tree. On average, this call has a constant cost overhead of \~20k gas on the sending chain.&#x20;

### 1. Saving the Inbox Merkle Tree State (Sending Chain)

On the sending chain, every epochPeriod, there is an opportunity to save a [snapshot](https://github.com/kleros/vea/blob/571a1ebbf897fdc29790f4737b70661f292b7aa5/contracts/src/arbitrumToEth/VeaInboxArbToEth.sol#L125) of the merkle root state in a mapping.

<figure><img src="/files/hZR0Akvn5pAxx2LtF3tW" alt=""><figcaption></figcaption></figure>

### 2. Claiming The Inbox State (Receiving Chain)

On the receiving chain, claims can be made about the snapshot of the merkle root taken in the inbox contract on the sending chain. This state root represents all messages which have been sent through Vea. Claims require a deposit in eth. If the claim is honest, the claimer will be returned their deposit. If the claim is otherwise successfully challenged, then half the deposit is burned and the other half rewards the challenger. There is a mandatory burn to avoid the zero-cost delay-grief sybil problem where the claimer and challenger could be the same entity.&#x20;

<figure><img src="/files/VQGH8YqrbNcCcJ2TZr5g" alt=""><figcaption></figcaption></figure>

There are some stroboscopic effects due to the frequency of bridge operation --- snapshots can be taken at the beginning of an epoch or the end. To ensure sufficient time to claim and to ensure that challengers know the L2 state, we introduce a claim delay of roughly an epochPeriod. Only after this delay can claims be made about a past epoch, at which time the L2 state during that epoch is known.

### Challenge Period

<figure><img src="/files/TOmk6rKba6LoSXsP1QYK" alt=""><figcaption></figcaption></figure>

Once a claim is made a challenge period begins. We expect a testnet and mainnet challenge period of several hours. During this period, anyone can challenge a claim by leaving a deposit. We call native bridges to resolve the disputed claim, and the honest party recieves half the deposit as a reward.

#### Censorship Test

The idea is that ETH proof-of-stake consensus chains, eg Ethereum mainnet and Gnosis Chain, contain enough information in block headers to make statistical conclusions about censorship --- critical when determining safe challenge period in optimistic mechanisms such as Vea. The failure mode of optimistic mechanisms involves censorship of honest challengers. There are multiple types of censorship.

There is "weak" censorship where block producers refuse to include your transaction. This is the type of censorship experienced by Tornado Cash. For example, if 90% of block producers censor Tornado Cash transactions, then on average, transactions involving Tornado Cash will take 10 times as long as usual. Eventually a non-censoring block producer is chosen to propose a block.

<figure><img src="/files/aEbxgWTXfE5ZWtxEt13i" alt=""><figcaption><p>Tornado Cash censoring blocks</p></figcaption></figure>

On the other hand, there is "strong" censorship where block producers themselves are censored and [reorged](https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/attack-and-defense/#reorgs) out. This is the type of censorship Optimistic mechanism designers are worried about.

Since Ethereum transitioned to proof of stake, there are new methods to detect "strong" censorship. Ed Felten, the Chief Scientist at Offchain Labs, [proposed](https://ethresear.ch/t/reducing-challenge-times-in-rollups/14997) such a method introducing a 'censorship test'.

The main idea is that in proof of stake, blocks are produced like clockwork. There are slots for validators to propose new blocks. If the validator misses their slot for block proposal, or if the block is reorged out, there will be an empty slot without a block. Luckily, there is enough information in block headers on-chain to deduce the number of missing blocks. When a block is missing, the block number does not increment, but the timestamp will increment with the slot time.

<figure><img src="/files/aENiYxfEDZ1AAhh0moDh" alt=""><figcaption><p>Missing Block Example</p></figcaption></figure>

For example, on Ethereum, the slot time is 12 seconds. At t0 = 0, block 0 is proposed. At t1 = 12 seconds, Block 1 is proposed. In the next slot, the block is missing. At t3, Block 2 is proposed, but the block timestamp is 36, taking into account the missing block slot.

By comparing the difference in timestamp with the difference in blocknumber, we can detect missing blocks.

```
uint256 expectedBlocks = (block.timestamp - t0) / SLOT_TIME;
```

With this missing block information available on-chain, we can develop a censorship test based on statistical assumptions. If we assume some some p percent of validators do not censor transactions, then we can calculate given some risk tolerance the chances of censorship during a challenge period.

The research [post](https://ethresear.ch/t/reducing-challenge-times-in-rollups/14997) states the test best,

<figure><img src="/files/8DG08wH5RtMDzIt6jEdK" alt=""><figcaption><p><a href="https://ethresear.ch/t/reducing-challenge-times-in-rollups/14997">https://ethresear.ch/t/reducing-challenge-times-in-rollups/14997</a></p></figcaption></figure>

For the purposes of the censorship test, one assumes that every missing block is a malicious block reorganization. This means, during times of poor network performance, for example following hard forks, the censorship test is likely to fail, even if 'censorship' is not on-going and the missing blocks are due to validator client implementation instability. Notice in the eth research post calculation example, that 4 missing blocks our of 225 slots is a missing block rate of \~1.7%. Notice in the graph below that this threshold of 1.7% is exceeded twice in Ethereum's history. Once after the [shapella](https://arbitrum.notion.site/17-relayers-are-failing-with-the-prysm-client-post-Capella-Shanghai-2023-04-12-mainnet-0ef5ccd795e54ae4894fa695f1a3e70b) hardfork, and again [recently](https://offchain.medium.com/post-mortem-report-ethereum-mainnet-finality-05-11-2023-95e271dfd8b2).

For Vea, any claim which fails the censorship test during the challenge period cannot be optimistically verified. Instead, to resolve the claim (to refund the claimer's deposit), we must call the native bridges to relay the snapshot saved in the sending chain to compare with the claim. This means under normal network conditions, Vea operates optimistically, but during periods of poor network performance for example following hardforks, Vea slows down and uses native bridges.

<figure><img src="/files/JQajjF7LpD0bopVkLHcZ" alt=""><figcaption><p><a href="https://ethereumverse.vercel.app/">https://ethereumverse.vercel.app/</a>, first 'bump' shapella hardfork, second 'bump' recent finality issue</p></figcaption></figure>

Since [Ethereumverse](https://twitter.com/gnosischain/status/1633155083843039242) chains like Ethereum mainnet and Gnosis Chain share the same consensus mechanism, this censorship test applies equally well adjusting for the different slot time.&#x20;

On Etheruem mainnet we are considering a missingBlock threshold of 601 in 7200 slots (8%) assuming atleast 10% of validators are non-censoring and a risk tolerance of 1:1 million.&#x20;

On Gnosis Chain we are considering a missingBlock threshold of 3207 in 17280 slots (18.5%) assuming atleast 20% of validators are non-censoring and a risk tolerance of 1:1 million.&#x20;

You can test our censorship parameter calculator [here](https://colab.research.google.com/drive/1-hcsqzQZX2OZouVfJZJ7tCjSQJ9Dh5Ay?usp=sharing).

### Optimistic Verification

<figure><img src="/files/vUi3K0dpTWThThsSgLGB" alt=""><figcaption><p>Vea Optimistic Verification Sequence Diagram</p></figcaption></figure>

### Happy Path

If the claim is unchallenged and the censorship test passes, the claim can be optimistically [verified](https://github.com/kleros/vea/blob/2410617e6e6c243bc3108059c39703350031ead2/contracts/src/arbitrumToEth/VeaOutboxArbToEth.sol#L178).

<figure><img src="/files/qRQxeppQ7dMapKowBn8Q" alt=""><figcaption></figcaption></figure>

### Unhappy Path

Claims can be challenged by leaving a deposit. Disputed claims are resolved by calling native bridges. In the case of optimistic rollups as intermediary or source chains for the native bridge routing, the resolution period can be 7-8 days, waiting for the latency of the 1 week challenge period of optimistic rollups.

<figure><img src="/files/EoMg8uDodMfSHGZQBK2t" alt=""><figcaption></figcaption></figure>

### Delay Griefs

<figure><img src="/files/XWTDfthJH21nsG8OjrQp" alt=""><figcaption></figcaption></figure>

Vea is a permissionless protocol. Anyone can make claims or challenges. As a result, Vea can be delay griefed. Malicious actors can slow down the bridge by making false claims up to a maximum delay of the latency of the native bridges. Since the claimer and challenger could be the same entity, to ensure that delays incur real costs, half of the deposit from proven dishonest actors are burned. The griefing factor is the deposit / 2 per epochPeriod --- in other words, the cost of delaying Vea by 1 epoch period is half a deposit. The maximum delay possible is the latency of calling native bridges to resolve the disputes.

The potential reward for honest actors, half the deposit, must be sufficient to pay for gas fees to resolve the claim, calling native bridges, even in high gas environments. For example, assuming 100k mainnet gas to resolve claims from an L2, given 10,000 gwei gas, the deposit should be at least 2 eth.

**How do we set a reasonable deposit?**

When bridging from Optimistic rollups, the native bridge takes at least 7 days. 1 day extra in case of L2 censorship by the sequencer (transactions can be force included on L1 after 24 hours). Add some margin for censorship by block producers or potentially of block producers, and the time to resolve disputes over vea inbox stateroots can take \~10 days --- atleast 7 days for Optimistic Rollup challenge period, an extra day in case of L2 censorship, and some buffer time.

An honest actor needs enough liquidity to challenge fraudulent claims. Given an epochPeriod and liquidity budget we can find the deposit size by

deposit = liquidity budget / (10 days / epochPeriod)

eg. suppose epochPeriod = 6 hours and liquidity budget = 800 eth. Then 10 days / (6 hours/ epoch ) = 40 epochs. In other words, an honest actor needs enough liquidity to cover 40 epochs of deposits to prevent an attacker. So finally, we calculate a reasonable deposit given the budget as

deposit = 800 eth / 40 = 20 eth.

### What happens if the L2 sending chain experiences some failure?

All most all rollups today are in "[stage 0](https://ethereum-magicians.org/t/proposed-milestones-for-rollups-taking-off-training-wheels/11571)" with training wheels on. As a result of the permissioned operation of L2s, the L2s lack liveliness guarantees. There's no guarantee that new L2 blocks will be produced by the permissioned set of L2 block producers, meaning there is no guarantee that the native bridges are available --- eg. L2 users are stuck.

On Arbitrum, If the validators stop producing blocks for 7 days, the [permissioning](https://github.com/OffchainLabs/nitro-contracts/blob/08ac127e966fa87a4d5ba3d23cd3132b57701132/src/rollup/RollupUserLogic.sol#L55) of the validator set is dropped, however the arbitrum protocol today is delay griefable, so there is a possibility that Arbitrum grinds to a halt, or the Arbitrum contracts are arbitrarily upgraded by the security council multisig.

For these reasons, we can't assume that challenged claims in the vea outbox can ever be resolved. timeoutEpochs defines the number of epochs of bridge inactivity, after which the vea outbox is considered [shutdown](https://github.com/kleros/vea/blob/2410617e6e6c243bc3108059c39703350031ead2/contracts/src/arbitrumToEth/VeaOutboxArbToEth.sol#L80), and all pending claims and challenges can withdraw their deposits. For example, 3 weeks might be a healthy value when bridging from optimistic rollup L2s.

### Summary

<figure><img src="/files/ChbrHligqlPnBMe0xaoy" alt=""><figcaption></figcaption></figure>

* **Vea is inclusive** Vea is a **permissionless** system: anyone can participate as a watcher or a validator.
* **Anyone can force correct execution** Anyone can take part in Vea, and it requires only one honest participant in the system to force the bridge in properly executing.
* **Vea is permanent** By permanent, we mean **immutable** and **ungoverned**. Once the parameters are deemed **safe**, the contracts are deployed based on them. There are **no potential changes**, no governance parameters, and no governance tokens.
* **Vea is a public good** Vea is a primitive that Kleros needs and have chosen to share with everyone to use, participate or build on top of it. They are **no fees**, **no rent-seeking**, and **no tokens**.
* **Vea is griefable** As an inclusive system, Vea exposes itself to griefing attacks introducing delays. But the cost increase linearly and is bound to a maximum of **7 days**, or **8 days in extreme cases** for optimistic systems.


# Implementation Details

Merkle Mountain Range implementation details for contract reviewers.

### Overview

The type of merkle tree implemented in the  [VeaInbox](https://github.com/kleros/vea/blob/571a1ebbf897fdc29790f4737b70661f292b7aa5/contracts/src/arbitrumToEth/VeaInboxArbToEth.sol) is sometimes referred to as a merkle mountain range ([MMR](https://github.com/opentimestamps/opentimestamps-server/blob/master/doc/merkle-mountain-range.md)). Without additional context, merkle trees are usually understood to be perfectly balanced binary trees with a fixed height. MMRs on the other hand grow in height as more leafs are inserted, and can be represented by a set of merkle subtrees, referred to as merkle "mountain ranges" due to their shape, see diagram below.

<figure><img src="/files/h9JAg85ml6GUBmmSBTD0" alt=""><figcaption><p>Merkle Mountain Range representing 7 messages</p></figcaption></figure>

### Leaf Hashing

All leafs are double hashed to avoid [second preimage attacks](https://flawed.net.nz/2018/02/21/attacking-merkle-trees-with-a-second-preimage-attack/).

### Inbox Data Structure

Merkle mountain ranges efficiently represent the state of all messages with a limited amount of data made available in the vea inbox contract stored in the [inbox](https://github.com/kleros/vea/blob/571a1ebbf897fdc29790f4737b70661f292b7aa5/contracts/src/arbitrumToEth/VeaInboxArbToEth.sol#L33) variable.

```
bytes32[64] public inbox; // stores minimal set of complete subtree roots of the merkle tree to increment.
```

The [inbox](https://github.com/kleros/vea/blob/c78180985507611b3f6b69c2863a7a36e1daed47/contracts/src/arbitrumToEth/VeaInboxArbToEth.sol#L50) represents the 'mountain ranges' or subtrees of varying height. eg inbox\[4] = root of a merkle tree of size 2^4 = 16 leaves.

The inbox state for the above merkle tree example with 7 messages is given by the table below.

<table><thead><tr><th width="101">Count</th><th width="201" align="center">Inbox[2]</th><th width="180" align="center">Inbox[1]</th><th align="center">Inbox[0]</th></tr></thead><tbody><tr><td>0b101</td><td align="center">H(1,4)</td><td align="center">H(5,6)</td><td align="center">H(7)</td></tr></tbody></table>

where we use the notation H as a short-hand for:

* H(1,4) = root of merkle tree representing messages m1, m2, m3, m4
* H(5,6) = root of merkle tree representing messages m5, m6
* H(7) = root of merkle tree representing message m7

### Notation

$$
H(n):= keccak(keccak(m\_n)))
$$

H(n) represents the n-th leaf which is the double hash of the n-th message m\_n.

H(n,m) represents an interior node of the tree which is the merkle root representing the leaves H(n), H(n+1), ..., H(m).

The parent of a pair of nodes is calculated by sorting & concatenating the nodes, and hashing the result.

for example

$$
H(1,2):= keccak(H(1) \mathbin{||}H(2))
$$

$$
H(3,4):= keccak(H(3) \mathbin{||}H(4)
$$

$$
H(1,4):= keccak(H(1,2) \mathbin{||}H(3,4))
$$

Note that we should sort hash pairs before hashing. eg. if H(2) < H(1), then we would concatenate in the opposite order.

$$
H(1,2) = keccak(H(2) \mathbin{||}H(1))
$$

Above we neglect the sorting notation for brevity.

### Inbox State Examples & Properties

Another example, showing the inbox state step by step for 7 insertions.

<table><thead><tr><th width="106">Count</th><th align="center">Inbox[2]</th><th align="center">Inbox[1]</th><th align="center">Inbox[0]</th></tr></thead><tbody><tr><td>0b001</td><td align="center">---</td><td align="center">---</td><td align="center">H(1)</td></tr><tr><td>0b010</td><td align="center">---</td><td align="center">H(1,2)</td><td align="center">"H(1)"</td></tr><tr><td>0b011</td><td align="center">---</td><td align="center">H(1,2)</td><td align="center">H(3)</td></tr><tr><td>0b100</td><td align="center">H(1,4)</td><td align="center">"H(1,2)"</td><td align="center">"H(3)"</td></tr><tr><td>0b101</td><td align="center">H(1,4)</td><td align="center">"H(1,2)"</td><td align="center">H(5)</td></tr><tr><td>0b110</td><td align="center">H(1,4)</td><td align="center">H(5,6)</td><td align="center">"H(5)"</td></tr><tr><td>0b111</td><td align="center">H(1,4)</td><td align="center">H(5,6)</td><td align="center">H(7)</td></tr></tbody></table>

Note some properties about the on bits ("1s") in count:

* The fist set bit of count corresponds to the modified inbox index.
* The on bits of count indicate the minimal data to represent the tree.

For example, when count = 0b010, inbox\[0] is set to H(1). However inbox\[1] = H(1,2) which implicitly includes H(1), so inbox\[0] is not needed to encode the tree. From the perspective of data availability, we can forget about H(1) in the slot represented by inbox\[0]. For that reason it is represented in quotation marks and can be overwritten in future steps, reusing the dirty inbox slot for efficiency.

### Calculating the root example

To calculate the root, we hash together the data in each inbox slot corresponding to an on bit in count, from the lowest index to the highest.

<table><thead><tr><th width="96">Count</th><th width="96" align="center">Inbox[2]</th><th width="93" align="center">Inbox[1]</th><th width="96" align="center">Inbox[0]</th><th align="center">root</th></tr></thead><tbody><tr><td>0b001</td><td align="center">---</td><td align="center">---</td><td align="center">H(1)</td><td align="center">H(1)</td></tr><tr><td>0b010</td><td align="center">---</td><td align="center">H(1,2)</td><td align="center">"H(1)"</td><td align="center">H(1,2)</td></tr><tr><td>0b011</td><td align="center">---</td><td align="center">H(1,2)</td><td align="center">H(3)</td><td align="center">H( H(3) , H(1,2) )</td></tr><tr><td>0b100</td><td align="center">H(1,4)</td><td align="center">"H(1,2)"</td><td align="center">"H(3)"</td><td align="center">H(1,4)</td></tr><tr><td>0b101</td><td align="center">H(1,4)</td><td align="center">"H(1,2)"</td><td align="center">H(5)</td><td align="center">H( H(1,4) , H(5) )</td></tr><tr><td>0b110</td><td align="center">H(1,4)</td><td align="center">H(5,6)</td><td align="center">"H(5)"</td><td align="center">H( H(1,4) , H(5,6) )</td></tr><tr><td>0b111</td><td align="center">H(1,4)</td><td align="center">H(5,6)</td><td align="center">H(7)</td><td align="center">H(H(1,4),H(H(5,6),H(7)))</td></tr></tbody></table>

## Other resources

Here are some useful resources to better understand merkle mountain ranges. The notation and indices used in the below resources differs from the notation used in this document. Resources are meant to be illustrative and supplemental.

* [opentimestamps/opentimestamps-server](https://github.com/opentimestamps/opentimestamps-server/blob/master/doc/merkle-mountain-range.md)
* [mimblewimble/grin](https://github.com/mimblewimble/grin/blob/master/doc/mmr.md)


# Getting Started

### Integrating Vea

For each sending and receiving chain pair supported by Vea, there is a separate set of Vea contract [deployments](/introduction/deployment-addresses). For each chain and network, there is exactly one deployed contract.

* Sending Chain: [VeaInbox](https://github.com/kleros/vea/blob/571a1ebbf897fdc29790f4737b70661f292b7aa5/contracts/src/arbitrumToEth/VeaInboxArbToEth.sol) - Manages state of all messages sent through Vea.
* Receiving Chain: [VeaOutbox](https://github.com/kleros/vea/blob/571a1ebbf897fdc29790f4737b70661f292b7aa5/contracts/src/arbitrumToEth/VeaOutboxArbToEth.sol) - Manages optimistic game over inbox state

To integrate Vea, sender and receiver gateway contracts need to be deployed to interface with the Vea inbox and outbox.

<figure><img src="/files/jgNFSuA55VOzc4d9aTZi" alt=""><figcaption><p>Vea Integration with Gateways</p></figcaption></figure>

### 1. Sender Gateway

For each sending, receiving chain pair supported by Vea, there is a 'Vea Inbox' contract [deployed](/introduction/deployment-addresses) on the sending chain. Contracts send messages through Vea by calling the 'sendMessage(...)' function in the Vea Inbox.

```solidity
function sendMessage(address _to, bytes memory _data) 
```

* `_to` The address on the receiving chain to call
* `_data` The abi encoded calldata to pass with the function call.
  * e.g. abi.encode(arg1, arg2, arg3. . .)

The sender gateway can implement the ISenderGateway [interface](https://github.com/kleros/vea/blob/571a1ebbf897fdc29790f4737b70661f292b7aa5/contracts/src/interfaces/gateways/ISenderGateway.sol).

```solidity
interface ISenderGateway {
    function veaInbox() external view returns (IVeaInbox);

    function receiverGateway() external view returns (address);
}
```

where the IVeaInbox [interface](https://github.com/kleros/vea/blob/571a1ebbf897fdc29790f4737b70661f292b7aa5/contracts/src/interfaces/inboxes/IVeaInbox.sol) includes the function stub to send messages through Vea.

```solidity
interface IVeaInbox {
    /// @dev Sends an arbitrary message to receiving chain.
    /// Note: Calls authenticated by receiving gateway checking the sender argument.
    /// @param _to The cross-domain contract address which receives the calldata.
    /// @param _data The message calldata, abi.encode(...)
    /// @return msgId The index of the message in the inbox, as a message Id, needed to relay the message.
    function sendMessage(
        address _to,
        bytes memory _data
    ) external returns (uint64 msgId);
}
```

To send a message through Vea, the sending gateway should implement a function to call sendMessage(...) in the Vea Inbox. Here is an example [mock implementation](https://github.com/kleros/vea/blob/571a1ebbf897fdc29790f4737b70661f292b7aa5/contracts/src/test/gateways/SenderGatewayMock.sol).

```solidity

pragma solidity 0.8.18;

import "ISenderGateway.sol";

/// Sender Gateway
/// Counterpart of `ReceiverGatewayMock`
contract SenderGatewayMock is ISenderGateway {
    IVeaInbox public immutable override veaInbox;
    address public immutable override receiverGateway;

    event VeaMessageSent(uint64 msgId);

    constructor(IVeaInbox _veaInbox, address _receiverGateway) {
        veaInbox = _veaInbox;
        receiverGateway = _receiverGateway;
    }

    function sendMessage(uint256 _data) external {
        bytes memory data = abi.encode(_data);
        uint64 msgId = veaInbox.sendMessage(receiverGateway, data);
        emit VeaMessageSent(msgId);
    }
}
```

Notice that the sendMessage(...) call in the Vea Inbox returns a uint64 message id. This id is used to relay the message on the receiving chain. Your dapp will probably want to index these messages with an event like below to later relay the message.

```solidity
emit VeaMessageSent(msgId);
```

In this example, the sender gateway is sending some uint256 \_data. The Vea Inbox contract expects a bytes array encoding the calldata to be passed with the cross-chain call.

```solidity
bytes memory data = abi.encode(_data);
```

The data which you include in the cross-chain message depends on your specific application.&#x20;

### 2. Receiver Gateway

For each sending, receiving chain pair supported by Vea, there is a 'Vea Outbox' contract [deployed](/introduction/deployment-addresses) on the receiving chain. Receiver gateways receive messages from Vea by receiving calls in '[receiveMessage](https://github.com/kleros/vea/blob/571a1ebbf897fdc29790f4737b70661f292b7aa5/contracts/src/interfaces/gateways/IReceiverGateway.sol#L16)(...)' from the '[sendMessage](https://github.com/kleros/vea/blob/add723da87c885a4d939da396279daa5fd688677/contracts/src/arbitrumToEth/VeaOutboxArbToEth.sol#L243)(...)' function in the Vea Outbox.

```solidity
function sendMessage(
    bytes32[] calldata _proof,
    uint64 _msgId,
    address _to,
    address _from,
    bytes calldata _message)
```

* `_proof` The merkle inclusion proof
* `_msgId` The message id to relay
* `_to` The address to call
* `_from` The sender address  from the Vea Inbox chain
* `_data` The message data to relay

The [Vea SDK](broken://pages/LTEwjQIUWNsDD4GYQ23E) provides utility functions to calculate proofs and fetch message data to relay.

#### IReceiverGateway Function Specification

In order to implement a cross-chain call, you need to implement the `receiveMessage` of IReceiverGateway [interface](https://github.com/kleros/vea/blob/571a1ebbf897fdc29790f4737b70661f292b7aa5/contracts/src/interfaces/gateways/IReceiverGateway.sol).  Here's an example where we define the function `receiveMessage`.

```solidity
contract ReceiverGatewayMock is IReceiverGateway {
    /// Receive the message from the sender gateway.
    function receiveMessage(address msgSender, bytes _data) external override{
        (bool success, ) = address(this).call(data);
        require(success, "Internal call failed");
    }
}

interface IReceiverGateway {
    function veaOutbox() external view returns (address);
    
    function receiveMessage(address msgSender, bytes _data) external;
    
    function senderGateway() external view returns (address);
}
```

Note that Vea passes the msg.sender who called the sendMessage(...) function on the sending chain as the first argument and message `data` in second argument in `receiveMessage` function call. So the receiver must implement the `receiveMessage` function of IReceiverGateway.&#x20;

<pre class="language-solidity"><code class="lang-solidity">interface IReceiverGatewayMock is IReceiverGateway {
    /// Receive the message from the sender gateway.
<strong>    function receiveMessage(address msgSender, bytes _data) external;
</strong><strong>}
</strong></code></pre>

Here's a complete example of a ReceiverGateway.

```solidity
pragma solidity 0.8.18;

import "./IReceiverGatewayMock.sol";

/// Receiver Gateway Mock
/// Counterpart of `SenderGatewayMock`
contract ReceiverGatewayMock is IReceiverGatewayMock {
    address public immutable override veaOutbox;
    address public immutable override senderGateway;

    bytes public data;

    constructor(address _veaOutbox, address _senderGateway) {
        veaOutbox = _veaOutbox;
        senderGateway = _senderGateway;
    }

    modifier onlyFromAuthenticatedVeaSender(address messageSender) {
        require(veaOutbox == msg.sender, "Vea Bridge only.");
        require(messageSender == senderGateway, "Only the sender gateway is allowed.");
        _;
    }

    /// Receive the message from the sender gateway.
    function receiveMessage(
        address messageSender,
        bytes _data
    ) external onlyFromAuthenticatedVeaSender(messageSender) {
        data = _data;
    }
}

```

#### Message Sender Authentication

The message sender of a cross-chain call is always the first argument of calldata passed to any receiver gateways.&#x20;

```solidity
modifier onlyFromAuthenticatedVeaSender(address messageSender) {
    require(veaOutbox == msg.sender, "Vea Bridge only.");
    require(messageSender == senderGateway, "Only the sender gateway is allowed.");
    _;
}

function receiveMessage(
    address messageSender,
    bytes _data
) external onlyFromAuthenticatedVeaSender(messageSender) {...}
```

Cross-chain call authentication requires checking that the msg.sender on the sending chain is the Vea Outbox contract, and checking the first argument of the call is equal to the sender gateway like shown in the modifier `onlyFromAuthenticatedVeaSender`.&#x20;

#### Customizing the data types sent between gateways

To support transfer of data types other than uint256 as shown in this example, simply encode those data types into a bytes array to pass in the SenderGateway when calling `sendMessage(...)` in the Vea Inbox.

For example, to support a call passing data of a string, uint64, and a bytes array, the ISenderGateway would instead implement&#x20;

```solidity
function sendMessage(string memory _data1, uint64 _data2, bytes memory _data3) external {
    bytes memory data = abi.encode(_data1, _data2, _data3);
    uint64 msgId = veaInbox.sendMessage(receiverGateway, data);
    emit VeaMessageSent(msgId);
}
```

and the IReceiverGateway interface would appropriately implement the `receiveMessage` function selector to decode the data types string, uint64, and bytes.

```solidity
contract ReceiverGatewayMock is IReceiverGateway {
    /// Receive the message from the sender gateway.
    function receiveMessage(
        address msgSender,
        bytes calldata _data
    ) external override {
        (string memory decoded1, uint64 decoded2, bytes memory decoded3) = 
        abi.decode(data, (string, uint64, bytes));
    }
}
```

### 3. Relaying your message

Once messages are bridged by Vea, which can take hours to days depending on the sending and receiving chain pairs, the messages can be relayed by providing a merkle proof of message inclusion in the Vea Outbox contract.


# Lightbulb Demo

https\://lightbulb-vea.vercel.app

The lightbulb [demo](https://lightbulb-vea.vercel.app/) demonstrates a simple dapp integrated with Vea. The switch lives on Arbitrum Sepolia and the Lightbulb lives on a different chain, either Chiado or Sepolia, and they communicate with Vea.

[Try it out](https://lightbulb-vea.vercel.app/)!

## User flow

### 1. Hit The Switch (Arbitrum Sepolia)

<figure><img src="/files/1jaCBSs76BMsXrGZro9q" alt=""><figcaption></figcaption></figure>

### 2. Wait for Vea

<figure><img src="/files/DXWFCqRvrqqYDpULLxNm" alt=""><figcaption></figcaption></figure>

The status of the Vea bridging process and whether your messages have been bridged can be tracked in [VeaScan](https://veascan.io/). You can find the messageId associated with the switch transaction in the event log.

<figure><img src="/files/Xz1Gg6G5QlvGjvM47djN" alt="" width="563"><figcaption><p>Switch Transaction event log</p></figcaption></figure>

When the Vea testnet has bridged your message, you can find your message in a recently bridged 'snapshot'.

<figure><img src="/files/4dz8EmfBLj9tdoSWNR7o" alt="" width="563"><figcaption><p>VeaScan successfully bridged message.</p></figcaption></figure>

### 3. Wait For Relayers

Now that the message to turn on the lightbulb is ready, we can relay it manually, or we can wait for relayers.

## The Final Result

<figure><img src="/files/enUAFweMlXa1AFN7Ms9q" alt=""><figcaption></figcaption></figure>


# Tutorial

🚧 Detailed guide coming soon. 🚧

## Goal

This quickstart guide walks through a [tutorial repo](https://github.com/kleros/vea-lightbulb-tutorial) to deploy a cross-chain application connecting a switch on Arbitrum Sepolia to a lightbulb on Chiado or Sepolia using the Vea devnet/testnet.

The tutorial will deploy contracts which implement the same cross-chain functionality as the Vea Lightbulb [demo](https://vea-lightbulb.vercel.app/).


# Validator

Validators are responsible for making and veryfiing claims of VeaInbox state on VeaOutbox. Validators keep Vea secure as it is a trust-minimized optimistically-verified bridge. The [trust model](https://vitalik.eth.limo/general/2020/08/20/trust.html) requires only 1 live honest verifier, similar to optimistic rollups.&#x20;

### Setup <a href="#prerequisites" id="prerequisites"></a>

1. Clone [repository](https://github.com/kleros/vea) <br>

   ```bash
   git clone git@github.com:kleros/vea.git
   ```
2. Create and populate `.env` in <kbd>./validator-cli</kbd> directory, follow [README.md](https://github.com/kleros/vea/blob/dev/validator-cli/README.md) for `env` variable details<br>

   ```bash
   cd validator-cli
   cp .env.example .env
   ```
3. Build and run validator image <br>

   ```bash
   docker compose build validator
   docker compose up validator
   ```


# Relayer

Relayers are responsible for executing message transactions on the VeaOutbox chain.

### Setup <a href="#prerequisites" id="prerequisites"></a>

1. Clone [repository](https://github.com/kleros/vea) <br>

   ```bash
   git clone git@github.com:kleros/vea.git
   ```
2. Create and populate `.env` in <kbd>./relayer-cli</kbd> directory, follow [README.md](https://github.com/kleros/vea/blob/dev/validator-cli/README.md) for `env` variable details<br>

   ```bash
   cd relayer-cli
   cp .env.example .env
   ```
3. Build and run validator image <br>

   ```bash
   docker compose build relayer
   docker compose up relayer
   ```


