What is an OFT on Sui?
An OFT on Sui is a Move package that extends the OApp functionality to enable crosschain token transfers. It integrates with Sui’s native coin type system (Coin<T>, Balance<T>, TreasuryCap<T>) while providing LayerZero’s omnichain capabilities.
This guide will walk you through deploying an OFT on Sui. To understand how OFTs integrate with Sui’s coin system and the differences between mint/burn and lock/unlock token management strategies, see Integration with Sui Coin System.
Deployment
OFT deployment on Sui uses a two-package pattern: your token + pure LayerZero OFT source.Mint/Burn Example
This deployment guide demonstrates the mint/burn approach, where you provide theTreasuryCap and the OFT mints/burns tokens during crosschain transfers. This works for both new tokens and existing tokens where you control the TreasuryCap.If you DON’T have the TreasuryCap (frozen, held by DAO, etc.), use the lock/unlock (adapter) approach instead. See Choosing Mint/Burn vs Lock/Unlock for details.- Sui CLI installed (via suiup)
- Node.js and npm for TypeScript SDK
- 1-2 SUI for gas fees
Step 1: Create and Deploy Your Token
Create token package:sources/myoft.move):
Step 2: Deploy LayerZero OFT Package
Deploy the pure LayerZero OFT source without modifications. Recommended approach (git dependencies):Move.toml with git dependencies:
OFTInitTicket (via oft_impl::init()).
Alternative: Deploy directly from the cloned repository using --with-unpublished-dependencies flag (requires all dependencies in correct relative paths).
Step 3: Initialize OFT via SDK
Consume the ticket using the OFT SDK. This example uses mint/burn initialization by passing theTreasuryCap:
Integration with Sui Coin System
OFTs integrate seamlessly with Sui’s native coin framework, using standard types for token management.Sui Coin Type System
The Sui framework provides these core types for token functionality:Coin<T>: Owned coin object with a value
Balance<T>: Storable value (can be held in structs)
TreasuryCap<T>: Authority to mint/burn coins
CoinMetadata<T>: Token information (name, symbol, decimals)
OFT Integration
The OFT uses these types:<phantom T> means:
Tis the coin type (e.g.,MY_COIN)phantom= T doesn’t appear in any field directly- Enables type safety without storing
Tvalues
OFT Types
Sui OFTs use a flexible enum pattern that supports two token management strategies, depending on whether you’re creating a new token or bridging an existing one.OFT Structure
The OFT uses a generic type parameter and includes built-in support for optional features:Treasury Enum
TheOFTTreasury<T> enum determines token management strategy:
Choosing Mint/Burn vs Lock/Unlock
| Model | When to Use | Initialization Method |
|---|---|---|
| Mint/Burn | You have/control the TreasuryCap<T> | oft.initOftMoveCall() with TREASURY parameter |
| Lock/Unlock | You DON’T have the TreasuryCap<T> | oft.initOftAdapterMoveCall() without TREASURY |
1. Mint/Burn
This model manages token supply by minting new tokens on the destination chain and burning them on the source chain. When to use:- You own or can obtain the
TreasuryCap<T>for the token - You’re comfortable with dynamic supply distribution across chains
- Works for both new tokens AND existing tokens where you control the TreasuryCap
TreasuryCap on Sui
On Sui,TreasuryCap<T> is an owned object that can be transferred between addresses. If you created a token previously or received the TreasuryCap from someone else, you can use the mint/burn model even for “existing” tokens. Only addresses with access to the TreasuryCap can mint and burn the token supply.- Send: Burns tokens on source chain (reduces total supply)
- Receive: Mints tokens on destination chain (increases total supply)
2. Lock/Unlock
The lock/unlock model enables omnichain bridging by escrowing tokens on the source chain and releasing them on the destination, maintaining fixed supply on Sui. When to use:- You DON’T have access to the
TreasuryCap<T>(frozen, held by DAO, or inaccessible) - Token supply on Sui must remain fixed
- You need to bridge a token where you lack mint/burn authority
- Send: Locks tokens in OFT’s escrow balance (removes from circulation)
- Receive: Releases tokens from escrow balance (returns to circulation)
Core Operations
The core operations of an Omnichain Fungible Token (OFT) on Sui enable seamless value transfer across multiple blockchains. At a high level, these consist of sending tokens to another chain and receiving them from peers, all while maintaining strict security and interoperability guarantees.Sending Tokens
Sending tokens is the primary function OFTs provide, allowing users to transfer assets from the current chain to a specified recipient on a different blockchain. This operation burns or locks tokens on the source chain, constructs a crosschain message, and leverages the LayerZero protocol to initiate delivery to the destination chain.Call<EndpointSendParam, MessagingReceipt>- Route through Endpoint, then confirmOFTSendContext- Context for confirming the send operation
- Debit tokens from sender’s coin (burns or escrows based on OFT type)
- Apply fee if configured, remove dust for decimal precision
- Build OFT message with recipient and amount in shared decimals
- Create Call to send via LayerZero Endpoint
- (Optional) Rate limiter tracks outbound flow
Receiving Tokens
Receiving tokens on Sui involves securely processing incoming crosschain messages, validating the source and payload, and minting or unlocking tokens to deliver them to the intended recipient.- Executor delivers Call object via Endpoint
- OApp validates Call came from authorized Endpoint and peer
- OFT decodes message to extract recipient and amount in shared decimals
- Converts amount to local decimals
- Credits tokens (mints or releases from escrow based on OFT type)
- Rate limiter tracks inbound flow
- Transfers credited tokens to recipient
lz_receive_with_compose() which additionally requires:
compose_queue: &mut ComposeQueuecomposer_manager: &mut OFTComposerManager
Decimal Precision
OFTs use local decimals (per-chain precision) and shared decimals (crosschain precision) to handle token transfers across blockchains with different decimal standards. For complete details on how this works, see OFT Technical Reference.Sui-Specific Constraint: u64 Balance Limit
Recommended Configuration for Sui
| Local Decimals | Max Total Supply | Recommendation |
|---|---|---|
| 6 | ~18.4 trillion | ✅ Recommended |
| 9 | ~18.4 billion | ✅ Recommended |
| 18 (EVM standard) | ~18 whole tokens | ❌ Avoid on Sui |
6 (default) for most use cases.
Registration with Endpoint
After initializing your OFT, you must register it with the LayerZero Endpoint to enable crosschain messaging.Using OFT SDK
- Creates
MessagingChannelshared object - Stores registry entry keyed by your package ID
- Auto-generates proper
lz_receive_infowith all required PTB instructions - No manual info generation needed!
0xfbece0b75d097c31b9963402a66e49074b0d3a2a64dd0ed666187ca6911a4d12
OFTComposerManager address on testnet: 0x90384f5f6034604f76ac99bbdd25bc3c9c646a6e13a27f14b530733a8e98db99
Configuration
After registration, configure your OFT to enable crosschain token transfers.Using OApp SDK for Configuration on Sui
All configuration is done through the base SDK’s OApp instance. Configure security settings before setting peers to open the pathway.Endpoint IDs
The examples below use EID30184 (Base Mainnet). For a complete list of endpoint IDs across all supported chains, see Deployed Contracts.- Set Libraries (recommended) - Custom send/receive message libraries
- Configure DVNs (recommended) - Send and receive verification
- Set Enforced Options (optional) - Minimum gas requirements
- Configure OFT Settings (optional) - Rate limits, fees
- Set Peer (required) - Opens pathway for messaging (call this last!)
Configuring Remote Chains to Send to Sui
When configuring OFTs on other chains (e.g., EVM, Solana) to send tokens to Sui, follow standard LayerZero configuration but note these Sui-specific requirements: 1. Use Package ID as Peer:- Sui’s
lz_receiveuses 2,000-5,000 MIST for computation - Use 5,000 gas units for safe buffer
- No
msg.valueneeded (Sui handles storage internally)
Example Usage
Sending Tokens (TypeScript SDK)
Best Practices & Troubleshooting
Deployment:- Use pure LayerZero OFT source without modifications
- Always wait for transaction finality:
await client.waitForTransaction({ digest }) - Use SDK factory:
sdk.getOApp(packageId)(nevernew OApp(...)) - Use SDK address exports with
Stage.MAINNET(no hardcoded addresses)
- Test with small amounts before production
- Validate peer addresses match package IDs (not object IDs)
- Configure DVNs before setting peers
- Only deploy one OFT Adapter per token
oapp_registry::get_messaging_channel abort code: 1→ Using object ID instead of package ID as peerInvalidBCSBytes in command 0→ UseOAppUlnConfigBcs.serialize()for DVN configUnusedValueWithoutDrop→ Useoft.registerOAppMoveCall()for proper lz_receive_info
Next Steps
- OFT SDK Documentation - Complete SDK methods and TypeScript integration
- Configuration Guide - DVN, executor, and gas configuration
- OApp Overview - Base messaging standard
- Technical Overview - Sui fundamentals and Call pattern
- Protocol Overview - Complete message workflows
- Troubleshooting - Common deployment issues