OFT
The_debit function in OFT.sol burns an amount of an ERC20 token, while _credit mints ERC20 tokens on the destination chain.

OFT.sol extends the base OApp.sol and inherits ERC20, providing both crosschain messaging and standard token functionality:
OFT Adapter
OFTAdapter.sol can be used for already deployed ERC20 tokens who lack mint capabilities, so that the _debit function calls safeERC20.transferFrom from a sender, while _credit calls safeERC20.transfer to a receiver.
OFTAdapter.sol provides token bridging without modifying the original ERC20 token contract:
For detailed technical information about transfer flows, decimal handling, and architecture patterns, see the OFT Technical Reference.
Find your chain
LayerZero supports OFT deployment on every EVM chain in the table below. Find your chain’s Endpoint ID (EID),EndpointId enum token, native chain ID, and a default RPC URL, then use those values wherever this guide references a chain (for example in your hardhat.config.ts networks block and your LayerZero config). The deployment flow is identical for every chain; only these identifiers change.
| Chain | Stage | EID | EndpointId | Native chain ID | RPC URL |
|---|---|---|---|---|---|
| 0G Mainnet | mainnet | 30388 | OG_V2_MAINNET | 16661 | https://evmrpc.0g.ai |
| Abstract Mainnet | mainnet | 30324 | ABSTRACT_V2_MAINNET | 2741 | https://api.mainnet.abs.xyz |
| Animechain Mainnet | mainnet | 30372 | ANIMECHAIN_V2_MAINNET | 69000 | (add your RPC) |
| Ape Mainnet | mainnet | 30312 | APE_V2_MAINNET | 33139 | https://rpc.apechain.com |
| Apex Fusion Nexus Mainnet | mainnet | 30384 | APEXFUSIONNEXUS_V2_MAINNET | 9069 | https://rpc.nexus.mainnet.apexfusion.org |
| Arbitrum Mainnet | mainnet | 30110 | ARBITRUM_V2_MAINNET | 42161 | https://arb1.arbitrum.io/rpc |
| Arbitrum Nova Mainnet | mainnet | 30175 | NOVA_V2_MAINNET | 42170 | https://arbitrum-nova.gateway.tenderly.co |
| Arc Mainnet | mainnet | 30417 | ARC_V2_MAINNET | 5042 | (add your RPC) |
| Astar Mainnet | mainnet | 30210 | ASTAR_V2_MAINNET | 592 | https://astar.api.onfinality.io/public |
| Astar zkEVM Mainnet | mainnet | 30257 | ZKATANA_V2_MAINNET | 3776 | https://rpc.startale.com/astar-zkevm |
| AULT Mainnet | mainnet | 30413 | AULT_V2_MAINNET | 904 | (add your RPC) |
| Avalanche Mainnet | mainnet | 30106 | AVALANCHE_V2_MAINNET | 43114 | https://api.avax.network/ext/bc/C/rpc |
| Base Mainnet | mainnet | 30184 | BASE_V2_MAINNET | 8453 | https://mainnet.base.org |
| Beam Mainnet | mainnet | 30198 | MERITCIRCLE_V2_MAINNET | 4337 | https://subnets.avax.network/beam/mainnet/rpc |
| Berachain Mainnet | mainnet | 30362 | BERA_V2_MAINNET | 80094 | https://rpc.berachain.com |
| Bitlayer Mainnet | mainnet | 30314 | BITLAYER_V2_MAINNET | 200901 | https://rpc.bitlayer.org |
| Blast Mainnet | mainnet | 30243 | BLAST_V2_MAINNET | 81457 | https://rpc.blast.io |
| BNB Smart Chain (BSC) Mainnet | mainnet | 30102 | BSC_V2_MAINNET | 56 | https://bsc.drpc.org |
| BOB Mainnet | mainnet | 30279 | BOB_V2_MAINNET | 60808 | https://rpc.gobob.xyz |
| Botanix | mainnet | 30376 | BOTANIX_V2_MAINNET | 3637 | https://rpc.botanixlabs.com |
| Bouncebit Mainnet | mainnet | 30293 | BOUNCEBIT_V2_MAINNET | 6001 | https://fullnode-mainnet.bouncebitapi.com |
| Camp Mainnet | mainnet | 30381 | CAMP_V2_MAINNET | 484 | https://rpc.camp.raas.gelato.cloud |
| Canto Mainnet | mainnet | 30159 | CANTO_V2_MAINNET | 7700 | https://canto-rpc.ansybl.io |
| Celo Mainnet | mainnet | 30125 | CELO_V2_MAINNET | 42220 | https://forno.celo.org |
| Chiliz Mainnet | mainnet | 30409 | CHILIZ_V2_MAINNET | 88888 | https://chiliz-mainnet.gateway.tatum.io |
| Citrea Mainnet | mainnet | 30403 | CITREA_V2_MAINNET | 4114 | https://rpc.mainnet.citrea.xyz |
| Codex Mainnet | mainnet | 30323 | CODEX_V2_MAINNET | 81224 | https://rpc.codex.xyz |
| Concrete | mainnet | 30366 | CONCRETE_V2_MAINNET | 12739 | (add your RPC) |
| Conflux eSpace Mainnet | mainnet | 30212 | CONFLUX_V2_MAINNET | 1030 | https://evm.confluxrpc.com |
| Core Mainnet | mainnet | 30153 | COREDAO_V2_MAINNET | 1116 | https://rpc.coredao.org |
| Cronos EVM Mainnet | mainnet | 30359 | CRONOSEVM_V2_MAINNET | 25 | https://evm.cronos.org |
| Cronos zkEVM Mainnet | mainnet | 30360 | CRONOSZKEVM_V2_MAINNET | 388 | https://mainnet.zkevm.cronos.org |
| Cyber Mainnet | mainnet | 30283 | CYBER_V2_MAINNET | 7560 | https://rpc.cyber.co |
| Degen Mainnet | mainnet | 30267 | DEGEN_V2_MAINNET | 666666666 | https://rpc.degen.tips |
| Derive Mainnet | mainnet | 30311 | LYRA_V2_MAINNET | 957 | https://rpc.lyra.finance |
| Dexalot Subnet Mainnet | mainnet | 30118 | DEXALOT_V2_MAINNET | 432204 | https://subnets.avax.network/dexalot/mainnet/rpc |
| DFK Chain | mainnet | 30115 | DFK_V2_MAINNET | 53935 | https://subnets.avax.network/defi-kingdoms/dfk-chain/rpc |
| Dinari Mainnet | mainnet | 30385 | DINARI_V2_MAINNET | 202110 | https://subnets.avax.network/dinari/mainnet/rpc |
| DM2 Verse Mainnet | mainnet | 30315 | DM2VERSE_V2_MAINNET | 68770 | https://rpc.dm2verse.dmm.com |
| Doma Mainnet | mainnet | 30393 | DOMA_V2_MAINNET | 97477 | https://doma.drpc.org |
| DOS Chain Mainnet | mainnet | 30149 | DOS_V2_MAINNET | 7979 | https://main.doschain.com |
| EDU Chain Mainnet | mainnet | 30328 | EDU_V2_MAINNET | 41923 | https://rpc.edu-chain.raas.gelato.cloud |
| Ethereal Mainnet | mainnet | 30391 | ETHEREAL_V2_MAINNET | 1380270412 | (add your RPC) |
| Ethereum Mainnet | mainnet | 30101 | ETHEREUM_V2_MAINNET | 1 | (add your RPC) |
| Etherlink Mainnet | mainnet | 30292 | ETHERLINK_V2_MAINNET | 42793 | https://node.mainnet.etherlink.com |
| EVM on Flow Mainnet | mainnet | 30336 | FLOW_V2_MAINNET | 747 | https://mainnet.evm.nodes.onflow.org |
| Fantom Mainnet | mainnet | 30112 | FANTOM_V2_MAINNET | 250 | https://rpcapi.fantom.network |
| Flare Mainnet | mainnet | 30295 | FLARE_V2_MAINNET | 14 | https://flare-api.flare.network/ext/C/rpc |
| Fraxtal Mainnet | mainnet | 30255 | FRAXTAL_V2_MAINNET | 252 | https://rpc.frax.com |
| Fuse Mainnet | mainnet | 30138 | FUSE_V2_MAINNET | 122 | https://fuse-pokt.nodies.app |
| Gate Layer Mainnet | mainnet | 30389 | GATELAYER_V2_MAINNET | 10088 | https://gatelayer-mainnet.gatenode.cc |
| Gensyn Mainnet | mainnet | 30412 | GENSYN_V2_MAINNET | 685689 | (add your RPC) |
| Gnosis Mainnet | mainnet | 30145 | GNOSIS_V2_MAINNET | 100 | https://rpc.gnosischain.com |
| Goat Mainnet | mainnet | 30361 | GOAT_V2_MAINNET | 2345 | https://rpc.goat.network |
| Gravity Mainnet | mainnet | 30294 | GRAVITY_V2_MAINNET | 1625 | https://rpc.gravity.xyz |
| Gunz Mainnet | mainnet | 30371 | GUNZ_V2_MAINNET | 43419 | https://rpc.gunzchain.io/ext/bc/2M47TxWHGnhNtq6pM5zPXdATBtuqubxn5EPFgFmEawCQr9WFML/rpc |
| Harmony Mainnet | mainnet | 30116 | HARMONY_V2_MAINNET | 1666600000 | https://api.s0.t.hmny.io |
| Hedera Mainnet | mainnet | 30316 | HEDERA_V2_MAINNET | 295 | https://mainnet.hashio.io/api |
| Hemi Mainnet | mainnet | 30329 | HEMI_V2_MAINNET | 43111 | https://rpc.hemi.network/rpc |
| Horizen Mainnet | mainnet | 30399 | HORIZEN_V2_MAINNET | 26514 | https://horizen.calderachain.xyz/http |
| Hubble Mainnet | mainnet | 30182 | HUBBLE_V2_MAINNET | 1992 | https://sanko-arb-sepolia.rpc.caldera.xyz/http |
| Humanity Mainnet | mainnet | 30382 | HUMANITY_V2_MAINNET | 6985385 | https://humanity-mainnet.g.alchemy.com/public |
| HyperEVM Mainnet | mainnet | 30367 | HYPERLIQUID_V2_MAINNET | 999 | https://hyperliquid.rpc.blxrbdn.com |
| Injective EVM Mainnet | mainnet | 30394 | INJECTIVEEVM_V2_MAINNET | 1776 | https://injectiveevm-rpc.polkachu.com |
| Ink Mainnet | mainnet | 30339 | INK_V2_MAINNET | 57073 | https://rpc-gel.inkonchain.com |
| IOTA EVM Mainnet | mainnet | 30284 | IOTA_V2_MAINNET | 8822 | https://rpc.ankr.com/iota_evm |
| Irys Mainnet | mainnet | 30408 | IRYS_V2_MAINNET | 3282 | https://mainnet-beta-rpc-2.irys.xyz/v1/execution-rpc |
| Japan Open Chain Mainnet | mainnet | 30285 | JOC_V2_MAINNET | 81 | https://rpc-1.japanopenchain.org:8545 |
| Kaia Mainnet (formerly Klaytn) | mainnet | 30150 | KLAYTN_V2_MAINNET | 8217 | https://public-en.node.kaia.io |
| Katana | mainnet | 30375 | KATANA_V2_MAINNET | 747474 | https://rpc.katana.network |
| Kava Mainnet | mainnet | 30177 | KAVA_V2_MAINNET | 2222 | https://evm.kava.io |
| Kite Mainnet | mainnet | 30406 | KITE_V2_MAINNET | 2366 | https://rpc.gokite.ai |
| Lens Mainnet | mainnet | 30373 | LENS_V2_MAINNET | 232 | https://rpc.lens.xyz |
| Lightlink Mainnet | mainnet | 30309 | LIGHTLINK_V2_MAINNET | 1890 | https://replicator.phoenix.lightlink.io/rpc/v1 |
| Linea Mainnet | mainnet | 30183 | ZKCONSENSYS_V2_MAINNET | 59144 | https://rpc.linea.build |
| Lisk Mainnet | mainnet | 30321 | LISK_V2_MAINNET | 1135 | https://rpc.api.lisk.com |
| Manta Pacific Mainnet | mainnet | 30217 | MANTA_V2_MAINNET | 169 | https://pacific-rpc.manta.network/http |
| Mantle Mainnet | mainnet | 30181 | MANTLE_V2_MAINNET | 5000 | https://rpc.mantle.xyz |
| MegaETH Mainnet | mainnet | 30398 | MEGAETH_V2_MAINNET | 4326 | (add your RPC) |
| Merlin Mainnet | mainnet | 30266 | MERLIN_V2_MAINNET | 4200 | https://merlin.drpc.org |
| Meter Mainnet | mainnet | 30176 | METER_V2_MAINNET | 82 | https://rpc.meter.io |
| Metis Mainnet | mainnet | 30151 | METIS_V2_MAINNET | 1088 | https://andromeda.metis.io/?owner=1088 |
| Mode Mainnet | mainnet | 30260 | MODE_V2_MAINNET | 34443 | https://mainnet.mode.network |
| Monad Mainnet | mainnet | 30390 | MONAD_V2_MAINNET | 143 | https://rpc1.monad.xyz |
| Moonbeam Mainnet | mainnet | 30126 | MOONBEAM_V2_MAINNET | 1284 | https://rpc.api.moonbeam.network |
| Moonriver Mainnet | mainnet | 30167 | MOONRIVER_V2_MAINNET | 1285 | https://rpc.api.moonriver.moonbeam.network |
| Morph Mainnet | mainnet | 30322 | MORPH_V2_MAINNET | 2818 | https://rpc.morphl2.io |
| Near Aurora Mainnet | mainnet | 30211 | AURORA_V2_MAINNET | 1313161554 | https://mainnet.aurora.dev |
| Neo X Mainnet | mainnet | 30414 | NEOX_V2_MAINNET | 47763 | (add your RPC) |
| Nexera Mainnet | mainnet | 30395 | NEXERA_V2_MAINNET | 7208 | https://rpc.nexera.network |
| Nibiru Mainnet | mainnet | 30369 | NIBIRU_V2_MAINNET | 6900 | https://evm-rpc.nibiru.fi |
| opBNB Mainnet | mainnet | 30202 | OPBNB_V2_MAINNET | 204 | https://opbnb-mainnet-rpc.bnbchain.org |
| OpenLedger Mainnet | mainnet | 30392 | OPENLEDGER_V2_MAINNET | 1612 | https://rpc.openledger.xyz |
| Optimism Mainnet | mainnet | 30111 | OPTIMISM_V2_MAINNET | 10 | https://mainnet.optimism.io |
| Orderly Mainnet | mainnet | 30213 | ORDERLY_V2_MAINNET | 291 | https://rpc.orderly.network |
| Otherworld Space Mainnet | mainnet | 30341 | SPACE_V2_MAINNET | 8227 | https://subnets.avax.network/space/mainnet/rpc |
| Peaq Mainnet | mainnet | 30302 | PEAQ_V2_MAINNET | 3338 | https://quicknode3.peaq.xyz |
| Pharos Mainnet | mainnet | 30407 | PHAROS_V2_MAINNET | 1672 | (add your RPC) |
| Plasma Mainnet | mainnet | 30383 | PLASMA_V2_MAINNET | 9745 | https://rpc.plasma.to |
| Plume Mainnet | mainnet | 30370 | PLUMEPHOENIX_V2_MAINNET | 98866 | https://rpc.plume.org |
| Polygon Mainnet | mainnet | 30109 | POLYGON_V2_MAINNET | 137 | https://polygon.drpc.org |
| Rari Chain Mainnet | mainnet | 30235 | RARIBLE_V2_MAINNET | 1380012617 | https://mainnet.rpc.rarichain.org/http |
| Rayls Mainnet | mainnet | 30415 | RAYLS_V2_MAINNET | 72957 | (add your RPC) |
| re.al Mainnet | mainnet | 30237 | REAL_V2_MAINNET | 111188 | https://rpc.realforreal.gelato.digital |
| Redbelly Mainnet | mainnet | 30402 | REDBELLY_V2_MAINNET | 151 | https://governors.mainnet.redbelly.network |
| Reya Mainnet | mainnet | 30313 | REYA_V2_MAINNET | 1729 | https://rpc.reya.network |
| Rise Mainnet | mainnet | 30401 | RISE_V2_MAINNET | 4153 | (add your RPC) |
| Robinhood Chain Mainnet | mainnet | 30416 | ROBINHOOD_V2_MAINNET | 4663 | (add your RPC) |
| Rootstock Mainnet | mainnet | 30333 | ROOTSTOCK_V2_MAINNET | 30 | https://mycrypto.rsk.co |
| Scroll Mainnet | mainnet | 30214 | SCROLL_V2_MAINNET | 534352 | https://rpc.scroll.io |
| Sei Mainnet | mainnet | 30280 | SEI_V2_MAINNET | 1329 | https://evm-rpc.sei-apis.com |
| Shimmer Mainnet | mainnet | 30230 | SHIMMER_V2_MAINNET | 148 | https://json-rpc.evm.shimmer.network |
| Silicon Mainnet | mainnet | 30379 | SILICON_V2_MAINNET | 2355 | https://silicon-mainnet.nodeinfra.com |
| Skale Mainnet | mainnet | 30273 | SKALE_V2_MAINNET | 2046399126 | https://mainnet.skalenodes.com/v1/elated-tan-skat |
| Somnia Mainnet | mainnet | 30380 | SOMNIA_V2_MAINNET | 5031 | (add your RPC) |
| Soneium Mainnet | mainnet | 30340 | SONEIUM_V2_MAINNET | 1868 | https://rpc.soneium.org |
| Sonic Mainnet | mainnet | 30332 | SONIC_V2_MAINNET | 146 | https://rpc.soniclabs.com |
| Sophon Mainnet | mainnet | 30334 | SOPHON_V2_MAINNET | 50104 | https://rpc.sophon.xyz |
| Stable Mainnet | mainnet | 30396 | STABLE_V2_MAINNET | 988 | (add your RPC) |
| Story Mainnet | mainnet | 30364 | STORY_V2_MAINNET | 1514 | https://rpc.ankr.com/story_mainnet |
| Subtensor EVM Mainnet | mainnet | 30374 | SUBTENSOREVM_V2_MAINNET | 964 | https://bittensor-lite-public.nodies.app |
| Superposition Mainnet | mainnet | 30327 | SUPERPOSITION_V2_MAINNET | 55244 | https://rpc.superposition.so |
| Tac | mainnet | 30377 | TAC_V2_MAINNET | 239 | https://rpc.ankr.com/tac |
| Taiko Mainnet | mainnet | 30290 | TAIKO_V2_MAINNET | 167000 | https://rpc.taiko.xyz |
| TelosEVM Mainnet | mainnet | 30199 | TELOS_V2_MAINNET | 40 | https://rpc.telos.net |
| Tempo Mainnet | mainnet | 30410 | TEMPO_V2_MAINNET | 4217 | (add your RPC) |
| Tenet Mainnet | mainnet | 30173 | TENET_V2_MAINNET | 1559 | https://rpc.ankr.com/tenet_evm |
| Tiltyard Mainnet | mainnet | 30238 | TILTYARD_V2_MAINNET | 710420 | https://subnets.avax.network/tiltyard/mainnet/rpc |
| Unichain Mainnet | mainnet | 30320 | UNICHAIN_V2_MAINNET | 130 | https://unichain.api.onfinality.io/public |
| Vana Mainnet | mainnet | 30330 | ISLANDER_V2_MAINNET | 1480 | https://rpc.vana.org |
| Viction Mainnet | mainnet | 30196 | TOMO_V2_MAINNET | 88 | https://viction.blockpi.network/v1/rpc/public |
| Worldchain Mainnet | mainnet | 30319 | WORLDCHAIN_V2_MAINNET | 480 | https://worldchain.drpc.org |
| X Layer Mainnet | mainnet | 30274 | XLAYER_V2_MAINNET | 196 | https://xlayerrpc.okx.com |
| Xai Mainnet | mainnet | 30236 | XAI_V2_MAINNET | 660279 | https://xai-chain.net/rpc |
| XDC Mainnet | mainnet | 30365 | XDC_V2_MAINNET | 50 | https://rpc.ankr.com/xdc |
| XPLA Mainnet | mainnet | 30216 | XPLA_V2_MAINNET | 37 | https://dimension-evm-rpc.xpla.dev |
| Zama Mainnet | mainnet | 30397 | ZAMA_V2_MAINNET | 261131 | (add your RPC) |
| Zircuit Mainnet | mainnet | 30303 | ZIRCUIT_V2_MAINNET | 48900 | https://mainnet.zircuit.com |
| zkSync Era Mainnet | mainnet | 30165 | ZKSYNC_V2_MAINNET | 324 | https://mainnet.era.zksync.io |
| zkVerify Mainnet | mainnet | 30386 | ZKVERIFY_V2_MAINNET | 1408 | https://vflow-rpc.zkverify.io |
| Zora Mainnet | mainnet | 30195 | ZORA_V2_MAINNET | 7777777 | https://rpc.zora.energy |
| Arbitrum Sepolia Testnet | testnet | 40231 | ARBSEP_V2_TESTNET | 421614 | https://sepolia-rollup.arbitrum.io/rpc |
| Avalanche Fuji Testnet | testnet | 40106 | AVALANCHE_V2_TESTNET | 43113 | https://api.avax-test.network/ext/bc/C/rpc |
| Base Sepolia Testnet | testnet | 40245 | BASESEP_V2_TESTNET | 84532 | https://base-sepolia-rpc.publicnode.com |
| Berachain Bepolia Testnet | testnet | 40371 | BEPOLIA_V2_TESTNET | 80069 | https://bepolia.rpc.berachain.com |
| BNB Smart Chain (BSC) Testnet | testnet | 40102 | BSC_V2_TESTNET | 97 | https://data-seed-prebsc-2-s1.bnbchain.org:8545 |
| Ethereum Holesky Testnet | testnet | 40217 | HOLESKY_V2_TESTNET | 17000 | https://holesky.drpc.org |
| Ethereum Sepolia Testnet | testnet | 40161 | SEPOLIA_V2_TESTNET | 11155111 | https://ethereum-sepolia-rpc.publicnode.com |
| EVM on Flow Testnet | testnet | 40351 | FLOW_V2_TESTNET | 545 | https://testnet.evm.nodes.onflow.org |
| Flare Testnet | testnet | 40294 | FLARE_V2_TESTNET | 114 | https://coston2.enosys.global/ext/C/rpc |
| Hedera Testnet | testnet | 40285 | HEDERA_V2_TESTNET | 296 | https://296.rpc.thirdweb.com |
| Hoodi Testnet | testnet | 40449 | HOODI_V2_TESTNET | 560048 | https://0xrpc.io/hoodi |
| HyperEVM Testnet | testnet | 40362 | HYPERLIQUID_V2_TESTNET | 998 | https://rpc.hyperliquid-testnet.xyz/evm |
| Optimism Sepolia Testnet | testnet | 40232 | OPTSEP_V2_TESTNET | 11155420 | https://optimism-sepolia-public.nodies.app |
| Polygon Amoy Testnet | testnet | 40267 | AMOY_V2_TESTNET | 80002 | https://rpc-amoy.polygon.technology |
| Tempo Testnet | testnet | 40439 | TEMPODEV1_V2_TESTNET | 42429 | https://tempo-testnet.drpc.org |
| Unichain Testnet | testnet | 40333 | UNICHAIN_V2_TESTNET | 1301 | https://sepolia.unichain.org |
| zkSync Sepolia Testnet | testnet | 40305 | ZKSYNCSEP_V2_TESTNET | 300 | https://sepolia.era.zksync.dev |
Installation
Below, you can find instructions for installing the OFT contract:OFT in a new project
To start using LayerZero OFT contracts in a new project, use the LayerZero CLI tool, create-lz-oapp. The CLI tool allows developers to create any omnichain application in <4 minutes! Get started by running the following from your command line:OFT in an existing project
To use LayerZero contracts in an existing project, you can install the OFT package directly:- npm
- yarn
- pnpm
- forge
LayerZero contracts work with both OpenZeppelin V5 and V4 contracts. Specify your desired version in your project’s
package.json:Custom OFT Contract
To build your own omnichain token contract, inherit fromOFT.sol or OFTAdapter.sol depending on whether you’re creating a new token or bridging an existing one.
Below is a complete example showing the key pieces you need to implement:
- OFT (New token)
- OFT Adapter (Existing token)
- Burning tokens on the source chain when sending
- Minting tokens on the destination chain when receiving
- Decimal precision conversion between different chains
- Unified supply management across all networks
Constructor
- Pass the Endpoint V2 address and owner address into the base contracts.
OFT(_name, _symbol, _lzEndpoint, _owner)binds your contract to the local LayerZero Endpoint V2 and registers the delegateOwnable(_owner)makes_ownerthe only address that can change configurations (such as peers, enforced options, and delegate)
- After deployment, the owner can call:
setConfig(...)to adjust library or DVN parameterssetSendLibrary(...)andsetReceiveLibrary(...)to override default librariessetPeer(...)to whitelist remote OFT addressessetDelegate(...)to assign a different delegate addresssetEnforcedOptions(...)to set mandatory execution options
Deployment and Wiring
After you finish writing and testing yourMyOFT contract, follow these steps to deploy it on each network and wire up the messaging stack.
1. Deploy Your OFT Contract
DeployMyOFT on each chain using either the LayerZero CLI (recommended) or manual deployment scripts.
- LayerZero CLI
- Manual Foundry
After running The LayerZero CLI provides automated deployment with built-in endpoint detection based on your The CLI will prompt you to:The CLI automatically:
pnpm compile at the root level of your example repo, you can deploy your contracts.Network Configuration
Before using the CLI, you’ll need to configure your networks inhardhat.config.ts with LayerZero Endpoint IDs (EIDs) and declare an RPC URL in your .env or directly in the config file:The key addition to a standard
hardhat.config.ts is the inclusion of LayerZero Endpoint IDs (eid) for each network. Check the Deployments section for all available endpoint IDs.hardhat.config.ts networks object:- Select chains to deploy to:
- Choose deploy script tags:
- Confirm deployment:
- Detects the correct LayerZero Endpoint V2 address for each chain
- Deploys your OApp contract with proper constructor arguments
- Generates deployment artifacts in
./deployments/folder - Creates network-specific deployment files (e.g.,
deployments/sepolia/MyOApp.json)
2. Wire Messaging Libraries and Configurations
Once your contracts are onchain, you must set up send/receive libraries and DVN/Executor settings so crosschain messages flow correctly.- LayerZero CLI
- Manual Foundry
The LayerZero CLI automatically handles all wiring via a single configuration file and command:Make sure your contract object’s This automatically handles:
Configuration File
In your project root, you can find alayerzero.config.ts file:contractName matches the named deployment file for the network under ./deployments/.Wire Everything
Run a single command to configure all pathways:- Fetching the necessary contract addresses for each network from metadata
- Setting send and receive libraries
- Configuring DVNs and Executors
- Setting up peers between contracts
- Applying enforced options
- All bidirectional pathways in your config
Usage
Once deployed and wired, you can begin sending tokens across chains.Send tokens
The OFT standard provides methods for quoting and sending tokens crosschain via the IOFT interface.quoteSend() - Get Transfer Fees
quoteOFT() - Get Detailed Transfer Quote
send() - Transfer Tokens
How send() Works Under the Hood
When you callsend(), it triggers a chain of calls through the LayerZero protocol:
- OFT Contract → Debits tokens
- LayerZero Endpoint → Routes the message to your configured MessageLib
- Message Library (SendUln302) → Requests verification/execution from configured DVNs/Executor
- Workers (DVNs + Executor) → Quote their fees for verification and execution services
- Fee Aggregation → Returns total
nativeFeeneeded for the transfer
- Send/Receive libraries - Set the MessageLibs for your contract
- DVNs - Select which DVNs verify your crosschain messages (at least one required)
- Enforced options OR caller options - Provide gas settings (globally or per call), or transactions fail with
LZ_ULN_InvalidWorkerOptions - Peers - Register destination OFT addresses for crosschain transfers
send() will work. They are configured during the “Deployment and Wiring” step above.
Trust Decisions
Using Managed Applications (e.g., Stargate): You trust the application team’s selected DVNs and Executor configurations.Deploying Your Own OFT: You select your trusted DVNs and Executors, giving you full control over your security assumptions.- LayerZero CLI
- Manual Foundry
The LayerZero CLI provides a convenient task for sending OFT tokens that automatically handles fee estimation and transaction execution.Required Parameters:The task automatically:
Using the Send Task
The CLI includes a built-inlz:oft:send task that:- Finds your deployed OFT contract automatically
- Quotes the gas cost using your OFT’s
quoteSend()function - Sends the tokens with the correct fee
- Provides tracking links for the transaction
--src-eid: Source endpoint ID (e.g., 40232 for Optimism Sepolia)--dst-eid: Destination endpoint ID (e.g., 40231 for Arbitrum Sepolia)--amount: Amount to send in human readable units (e.g., “1.5”)--to: Recipient address (20-byte hex for EVM)
--min-amount: Minimum amount to receive for slippage protection (e.g., “1.4”)--extra-options: Additional gas units for lzReceive, lzCompose, or receiver address--compose-msg: Arbitrary bytes message to deliver alongside the OFT--oft-address: Override the source OFT address (if not using deployment artifacts)
- Finds your deployed OFT contract from deployment artifacts
- Handles token approvals (for OFTAdapter)
- Quotes the exact gas fee needed
- Provides block explorer and LayerZero Scan links for tracking
Send tokens + call composer
Horizontal composability allows your OFT to trigger additional actions on the destination chain through separate, containerized message packets. Unlike vertical composability (multiple calls in a single transaction), horizontal composability processes operations independently, providing better fault isolation and gas efficiency.Benefits of Horizontal Composability
- Fault Isolation: If a composed call fails, it doesn’t revert the main token transfer
- Gas Efficiency: Each step can have independent gas limits and execution options
- Flexible Workflows: Complex multi-step operations can be broken into manageable pieces
- Non-Critical Operations: Secondary actions (like swaps or staking) can fail without affecting token delivery
Workflow Overview
- Token Transfer: OFT processes the token transfer in
_lzReceive()and credits tokens to the recipient - Compose Message: OFT calls
endpoint.sendCompose()to queue a separate composed message - Composer Execution: The composer contract receives the message via
lzCompose()and executes custom logic
Sending with ComposeMsg
When sending tokens with composed actions, set theto address to your composer contract and include your custom composeMsg:
Understanding the Message Encoding
When using composed messages, the OFT encodes yourcomposeMsg along with token transfer data. After processing the transfer, the destination OFT re-encodes this data and delivers it to your composer contract via endpoint.sendCompose().
For the complete message structures and codec functions, see the Message Encoding Reference documentation.
Execution Options for Composed Messages
Composed messages require gas for two separate executions:- Token Transfer (
lzReceive): Credits tokens and queues the composed message - Composer Call (
lzCompose): Executes your custom logic in the composer contract
Using the CLI with Composed Messages
Thelz:oft:send task supports composed messages via the --compose-msg and --extra-options parameters:
Implementing a Composer Contract
The composer contract must implementIOAppComposer to handle composed messages. Here’s a comprehensive example:
Key Security Considerations
- Endpoint Verification: Always verify
msg.sender == endpoint - OApp Authentication: Only accept messages from trusted OApps
- Message Validation: Validate all decoded parameters before execution
- Reentrancy Protection: Consider using
ReentrancyGuardfor complex operations
Extensions
The OFT Standard can be extended to support several different use cases, similar to the ERC20 token standard. Since OFT inherits from the base OApp contract, all OApp extensions and patterns are also available to OFT implementations, providing maximum flexibility for crosschain token applications. Below you can find relevant patterns and extensions:Rate Limiting
TheRateLimiter pattern controls the number of tokens that can be transferred crosschain within a specific time window. This is particularly valuable for OFTs to prevent abuse and ensure controlled token flow across chains.
Why Use Rate Limiting for OFTs?
- Prevent Token Drain Attacks: Protects against malicious actors attempting to rapidly drain tokens from a chain
- Regulatory Compliance: Helps meet compliance requirements for controlled cross-blockchain token transfers
- Supply Management: Maintains balanced token distribution across chains by limiting transfer velocity
- Risk Management: Reduces exposure to smart contract vulnerabilities or bridge exploits
Implementation
Inherit from bothOFT and RateLimiter in your contract:
Configuration
Set up rate limits per destination chain during deployment:Dynamic Rate Limit Management
Add functions to update rate limits post-deployment:Rate Limit Behavior
When a transfer exceeds the rate limit:- The transaction reverts with a rate limit error
- Users must wait for the time window to reset
- The limit resets based on a sliding window mechanism
Mint & Burn OFT Adapter
TheMintBurnOFTAdapter is a specialized adapter for existing ERC20 tokens that have exposed mint and burn functions. Unlike the standard OFTAdapter which locks/unlocks tokens, this adapter burns tokens on the source chain and mints them on the destination chain.
Key Differences from Standard OFTAdapter
| Feature | Standard OFTAdapter | MintBurnOFTAdapter |
|---|---|---|
| Token Supply | Locks/unlocks existing tokens | Burns/mints tokens dynamically |
| Multiple Deployments | Only one adapter per token globally | Multiple adapters can exist |
| Approval Required | Yes, users must approve adapter | No, uses mint/burn privileges |
| Token Mechanism | Escrow (locks tokens) | Non-escrow (burns/mints) |
When to Use MintBurnOFTAdapter
- Tokens with mint/burn capabilities: Your ERC20 already has
mint()andburn()functions - Dynamic supply management: You prefer burning/minting over locking mechanisms
- Reduced custody risk: Eliminate the risk of locked token supply running dry when using multiple adapters
Installation
To get started with a MintBurnOFTAdapter example, use the LayerZero CLI tool to create a new project:- Example
MintBurnOFTAdaptercontracts - Sample
ElevatedMinterBurnerimplementation - Deployment and configuration scripts
- Crosschain unit tests
Implementation
Create your mint/burn adapter by inheriting fromMintBurnOFTAdapter:
Token Requirements
You need a contract that implements theIMintableBurnable interface. This can be either:
Option 1: Token directly implements the interface
IMintableBurnable, use an intermediary contract:
- Use existing tokens without modification
- Control which contracts can mint/burn through operator management
- Maintain existing token governance while adding bridge functionality
Usage Flow
-
Sending tokens:
- User calls
send()on the MintBurnOFTAdapter - Adapter burns tokens from user’s balance
- LayerZero message sent to destination
- User calls
-
Receiving tokens:
- Destination adapter receives LayerZero message
- Adapter mints new tokens to recipient’s address
Security Considerations
TheMintBurnOFTAdapter requires careful access control since it can mint tokens:
Unlike standard OFTAdapter, you can deploy multiple MintBurnOFTAdapters for the same omnichain mesh.
OFT Alt
When the native gas token cannot be used to pay LayerZero fees, you can useOFTAlt which supports payment in an alternative ERC20 token.
Installation
To get started with an OFTAlt example, use the LayerZero CLI tool to create a new project:- Example
OFTAltcontracts with alternative fee payment EndpointV2Altintegration setup- Alternative fee token configuration
- Deployment and configuration scripts
- Crosschain unit tests with ERC20 fee payments
Implementation
Key Differences
- Fee Payment: Uses ERC20 tokens instead of native gas
- Approval Required: You must approve the OFT contract to spend your fee tokens
- Endpoint: Must use
EndpointV2Altinstead of standardEndpointV2
Using OFT Alt
Before sending messages, approve the fee token:OFT Alt is designed for chains where native gas tokens are not suitable for LayerZero fees, such as certain L2s or sidechains with alternative fee mechanisms.
Further Reading
For more advanced patterns and detailed implementations, see:- OApp Design Patterns - Additional messaging patterns
- Message Execution Options - Detailed options configuration
- OFT Technical Reference - Deep dive into OFT mechanics
