What is an OFT on Starknet?
An OFT on Starknet is a Cairo contract that extends the OApp functionality to enable crosschain token transfers. It integrates with Starknet’s native ERC20 system (token contracts, approvals, and metadata) while providing LayerZero’s omnichain capabilities. This guide will walk you through OFT concepts on Starknet; currently OFTAdapter and OFTMintBurnAdapter are available for deployment. To understand how OFTs integrate with Starknet’s ERC20 system and the differences between mint/burn and lock/unlock token management strategies, see Integration with Starknet ERC20 System.Integration with Starknet ERC20 System
OFT Types
Starknet provides three OFT variants for different use cases:AvailabilityAt the moment, OFTAdapter and OFTMintBurnAdapter are available on Starknet. OFT is not yet supported for deployment.
Decision Matrix
OFT Adapter
Use when bridging an existing token where you cannot grant mint/burn permissions to the adapter.How It Works
- Send: Locks tokens in the adapter contract
- Receive: Unlocks tokens from the adapter contract
- Liquidity Required: Adapter must hold sufficient token balance
OFT Mint/Burn Adapter
Use when bridging an existing token where you can grant mint/burn permissions to the adapter.How It Works
- Send: Burns tokens via minter contract
- Receive: Mints tokens via minter contract
- No Liquidity Required: Mint/burn eliminates liquidity constraints
Additional Features
The OFTMintBurnAdapter includes:- Rate Limiting: Control transfer volume per chain
- Fee Collection: Charge fees on transfers
- Pausability: Emergency pause functionality
- Role-Based Access: Granular permission control
- Upgradeability: Contract upgrade support
Role Management
The OFTMintBurnAdapter uses OpenZeppelin’s AccessControl with the following roles:Deployment
Before building an OFT, install the required dependencies. Prerequisites:- Scarb and Starknet Foundry installed (see Getting Started)
- Node.js and npm for installing LayerZero packages
- A funded Starknet account and RPC URL for deployment (see Getting Started)
- Deploy
ERC20MintBurnUpgradeableas your token - Deploy
OFTMintBurnAdapterwith the token address as botherc20_tokenandminter_burner - Grant the adapter’s address permission to mint/burn on the token contract
Step 1: Deploy ERC20MintBurnUpgradeable
For OFTMintBurnAdapter deployments, LayerZero provides a reference ERC20 token implementation with built-in mint/burn permissions: This contract:- Implements the
IMintableTokeninterface - Supports role-based access for mint/burn permissions
- Is upgradeable via OpenZeppelin’s
UpgradeableComponent
name(ByteArray) - Token name (use quoted string)symbol(ByteArray) - Token symbol (use quoted string)decimals(u8) - Token decimals (e.g., 18)default_admin(ContractAddress) - Address grantedDEFAULT_ADMIN_ROLE. You can set this to your address.
Step 2: Deploy OFTMintBurnAdapter
erc20_token: ERC20 token contract addressminter_burner: Minter/burner contract address (use the token address)lz_endpoint: LayerZero Endpoint address (0x0316d70a6e0445a58c486215fac8ead48d3db985acde27efca9130da4c675878for Sepolia,0x524e065abff21d225fb7b28f26ec2f48314ace6094bc085f0a7cf1dc2660f68for Mainnet)owner: Contract owner address (your deployer account)native_token: Fee payment token (STRK token address shown above)shared_decimals: Shared decimals across chains (u8, e.g., 6)
STRK Token AddressThe address
0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d is the STRK token contract on Starknet (same on both Sepolia testnet and Mainnet). This is used to pay LayerZero messaging fees.Deployment for Custom OFT
If you need to build a custom OFT contract (e.g., with additional logic or modifications), follow these steps to set up your project before declaring and deploying. You can use the OFTMintBurnAdapter as a starting point.Step 1: Install LayerZero Cairo Contracts
The LayerZero Cairo packages are currently published on NPM.- OFTMintBurnAdapter (Recommended)
- OFTAdapter
Step 2: Copy the contracts
Copy the contracts into your project’s directory:- OFTMintBurnAdapter (Recommended)
- OFTAdapter
Step 3: Modify dependency paths
In theScarb.toml, remove the parent directory references in the path fields:
Step 4: Make your customizations
Modify the contract as necessary.Step 5: Build, Declare, and Deploy
Core Operations
Sending Tokens
Step 1: Quote
Step 2: Get Messaging Fee
Step 3: Send
Decimal Precision
Token Amounts and u256
All token amounts in Starknet OFT contracts useu256, matching Solidity’s uint256 and OpenZeppelin’s Cairo ERC20 interface for crosschain compatibility.
Local vs Shared Decimals
OFTs use two decimal representations:Dust Removal
When converting from local to shared decimals, precision is lost (“dust”):Configuration
After deployment, configure your OFT to enable crosschain transfers. Use the SDK for DVN/executor config and set peers last.Endpoint IDsFor endpoint IDs and LayerZero contract addresses, see Deployed Contracts.
SDK Setup
Install the SDK dependencies:config.ts (or equivalent) and load your compiled artifact from target/release/*.contract_class.json:
Prerequisite: Set Delegate (required if configuring via external account)
Endpoint configuration calls (set_send_library, set_receive_library, set_send_configs, set_receive_configs) require the caller to be the OApp itself or an authorized delegate. If you’re configuring from an external account, set a delegate first (owner-only):
Step 1: Set Message Libraries (optional)
Use custom send/receive libraries when defaults are unavailable for your EID.Step 2: Configure DVNs (recommended)
Configure ULN settings for both send and receive. DVN addresses must be sorted ascending.Step 3: Configure Executor (recommended)
Executor settings apply to send direction.Step 4: Set Enforced Options (optional)
All OFT variants include theOAppOptionsType3Component for managing execution options. Use it to set minimum gas requirements per destination chain. If getOAppContract does not expose set_enforced_options, load your compiled artifact from target/dev/*.contract_class.json as shown in the SDK setup.
sncast, you can call the entrypoint directly:
<OPTIONS_BYTEARRAY>, pass a ByteArray expression (see the Starknet Foundry calldata transformation docs). With --arguments, use a raw ByteArray struct literal for arbitrary bytes, e.g. core::byte_array::ByteArray { data: array![0x..., 0x...], pending_word: 0x..., pending_word_len: 0 } (data are 31-byte chunks; pending_word_len is 0-30).
Example: lzReceive gas = 120000, value = 0:
Step 5: Set Peer (required, last)
Set the remote peer after security configuration. EVM addresses must be left-padded to 32 bytes.- Set delegate (required if configuring via external account)
- Set message libraries (optional)
- Configure DVNs (recommended)
- Configure executor (recommended)
- Set enforced options (optional)
- Set peer (required, last)
Events
OFT-Specific Events
Best Practices & Deployment Checklist
- Install dependencies - npm install LayerZero packages
- Choose OFT variant based on your token situation
- Build contract via
scarb build - Declare contract via
sncast declare - Deploy contract via
sncast deploywith--arguments - Verify contract via
sncast verifyusing Voyager or Walnut - Configure DVNs and executor for security (see Configuration Guide)
- Set enforced options for minimum gas
- Set peers last on both chains (bidirectional)
- Test on testnet before mainnet deployment
Next Steps
- Configuration Guide - DVN and security setup
- Protocol Overview - Message lifecycle
- Technical Reference - Deployment tooling
- Troubleshooting - Common errors