Skip to main content
The Omnichain Application (OApp) standard provides the foundational building blocks for crosschain messaging on Starknet. OApps can send arbitrary data to any supported chain and receive messages from other chains.

What is an OApp on Starknet?

An OApp on Starknet is a Cairo contract that:
  1. Integrates with LayerZero via the OAppCoreComponent
  2. Sends messages through the Endpoint’s send function
  3. Receives messages by implementing the OAppHooks trait
  4. Manages peers (trusted remote OApps on other chains)

Differences from EVM OApps

Installation

Step 1: Install LayerZero Cairo Contracts

Step 2: Configure Scarb.toml

Tool versionsUse Scarb 2.14.0 and Starknet Foundry 0.53.0. Mismatched versions can cause class hash mismatch errors during sncast declare.

Step 3: Create Project Structure

lib.cairo


Step 4: Configure snfoundry.toml

Create a snfoundry.toml with your account name and RPC URL. See Starknet Guidance for the full configuration reference and RPC version compatibility notes.

Deployment

Step 1: Build

Build artifacts are generated in target/dev/ by default.

Step 2: Declare

Step 3: Deploy

Network flagIf you set url in snfoundry.toml, omit --network (sncast will reject it).
Using —argumentsThe --arguments flag allows passing constructor arguments in a human-readable format. sncast automatically serializes them based on the contract’s ABI. For more details, see Calldata Transformation.

Step 4: Verify

For more verification options, see the Starknet Foundry verification guide.

Step 5: Configure

Bytes32 EncodingPeer addresses are stored as Bytes32 (a struct containing a u256). For EVM addresses (20 bytes), left-pad with zeros to 32 bytes.Calldata format for set_peer(eid: u32, peer: Bytes32):
  1. eid - endpoint ID as hex (e.g., 0x7595 = 30101 for Ethereum Mainnet)
  2. peer.value.low - lower 128 bits of the padded address
  3. peer.value.high - upper 128 bits of the padded address
Use --calldata with space-separated hex values (not --arguments) for complex types like Bytes32.

Working Example: Minimal OApp

A minimal OApp on Starknet:

Required Components

OAppCoreComponent

The core LayerZero integration:
Provided Functions:

OwnableComponent

OpenZeppelin’s ownership management:

How OApp Messaging Works

Peer Configuration: Establishing Trust

Peers must be set bidirectionally for two OApps to communicate:

Setting a Peer

The OAppCoreComponent provides set_peer automatically when you embed OAppCoreImpl. You call it directly on your deployed contract:
Internally, the component implements it as:

Peer Address Format

Peers are stored as Bytes32 for cross-VM compatibility:

Bidirectional Setup

Both sides must set peers before messages can flow.

Sending Messages

Step 1: Quote the Fee

Step 2: Build Options

Options specify execution parameters on the destination chain:

Step 3: Send the Message

The _lz_send function handles fee payment internally. It expects the caller to have approved the OApp contract (not the endpoint) to spend their tokens. The function will:
  1. Transfer tokens from caller to the contract
  2. Approve the endpoint to spend the tokens
  3. Send the message via the endpoint

Complete Send Example


Receiving Messages

Implementing OAppHooks

The OAppHooks trait defines how your OApp handles incoming messages:

Origin Verification

The OAppCore ensures only the Endpoint can call lz_receive and that the sender matches the trusted peer:

Events

Standard OApp Events

DelegateSet EventThe DelegateSet event is emitted by the Endpoint contract (not the OApp) when set_delegate is called. Listen for it on the Endpoint address, not your OApp.

Custom Events

Add your own events for tracking:

Network Addresses


Next Steps