LayerZero provides IOTA L1 Move Packages that can communicate with the equivalent Solidity Contract Libraries and Solana Programs deployed on other chains. These packages, like their Solidity and Rust counterparts, simplify calling the LayerZero Endpoint, provide message handling, interfaces for protocol configurations, and other utilities for interoperability:
- Omnichain Fungible Token (OFT): extends OApp with functionality for handling omnichain token transfers using IOTA L1’s coin framework.
- Omnichain Application (OApp): the base package utilities for omnichain messaging and configuration.
Differences from the Ethereum Virtual Machine
The full differences between Solidity/EVM and IOTA L1/Move are significant. For comprehensive guides, see:Skip this section if you already feel comfortable working with the IOTA L1 blockchain and its object model.
Object Model vs Account Model
The most fundamental difference is how state is organized: EVM (Account Model):Writing Smart Contracts on IOTA
To create a new ERC20 token on an EVM-compatible blockchain, a developer inherits and redeploys the ERC20 contract:- Packages: Published Move code (immutable)
- Objects: State containers with unique IDs
- Capabilities: Authorization objects
- No redeploy: Package is published once, objects created many times
- No inheritance: Use composition and capabilities instead
- Object ownership: State has explicit ownership (address, shared, immutable)
- Type safety: Move’s type system prevents many runtime errors
Object Ownership Types
IOTA’s ownership model determines who can access and modify objects. Understanding these types is essential for building LayerZero applications:
OApp on IOTA:
Capabilities vs msg.sender
What are Capabilities? Capabilities are special owned objects that grant specific permissions. Owning a capability object proves you have authorization to perform certain operations. EVM Authorization usesmsg.sender:
- Transferable: Can give capabilities to other addresses
- Composable: Capabilities can be stored in other objects
- Type-safe: Different capabilities for different permissions
- No spoofing: Can’t fake capability ownership
Programmable Transaction Blocks
While EVM executes one function call per transaction, IOTA enables complex multi-step workflows in a single atomic transaction:- Quote fees
- Send message
- Route through Endpoint/ULN/Workers
- Confirm and extract receipt
- All in one PTB, atomically
No Dynamic Dispatch (Call Pattern)
EVM can dynamically call contracts:Call object has no abilities at all:
- No
dropability → Can’t be ignored (must be consumed) - No
storeability → Can’t be saved in structs - No
copyability → Can’t be forged or copied (prevents reentrancy) - No
keyability → Can’t be stored globally in the ledger
Call must be explicitly destroyed before the transaction ends, routing through the PTB to the destination module. This achieves similar functionality to dynamic dispatch while maintaining type safety and preventing reentrancy attacks.
Prerequisites
Before you start building, you’ll need to set up your development environment.Install IOTA CLI
Install the IOTA CLI from the official repository:Install Node.js and TypeScript SDK
For PTB construction and SDK usage in your OApp/OFT project:Set Up IOTA Wallet
Create or import a wallet:Get Testnet IOTA
For testing on IOTA testnet, see IOTA Faucet documentation:Understanding Package IDs vs Object IDs
One of the most important concepts for LayerZero on IOTA is the distinction between package IDs and object IDs:
Finding Package ID:
- Peer addresses = Package ID (where code is deployed)
- Not Object ID (instance of OApp/OFT)
Understanding the Registry System
When you deploy and register an OApp with the LayerZero Endpoint, understanding the registry architecture is crucial: What happens during registration:- Endpoint stores your package ID in its registry (not object ID)
- MessagingChannel.oapp field = your package ID
- Remote chains send messages to your package ID
- Endpoint looks up package ID → finds your MessagingChannel → routes message
CallCap and Package Identity
LayerZero OApps use Package CallCaps (not Individual CallCaps):callCap.id()returns your package address- Registry keys by this package address
- All lookups expect package address
- Remote chains must use this as peer address
Next Steps
Choose your path:Build an OApp
For custom crosschain logic:- OApp Overview - Architecture and patterns
- OApp Protocol Details - Deep technical dive
- Technical Overview - IOTA fundamentals
Build an OFT
For crosschain tokens:- OFT Overview - Token architecture
- OFT SDK - TypeScript SDK integration and methods
- Configuration Guide - Security and DVN setup
Understand the Protocol
For protocol-level understanding:- Technical Overview - VM architecture and Call pattern
- Protocol Overview - Complete message workflows
- OFT SDK - Available SDK methods and patterns
Get Help
- Troubleshooting - Common issues
- FAQ - Frequently asked questions
- Discord - Community support