What is an OFT on Stellar?
An Omnichain Fungible Token (OFT) on Stellar is a Soroban smart contract that enables crosschain token transfers via the LayerZero protocol. It extends the OApp contract with token handling logic for debiting (lock/burn) and crediting (unlock/mint) tokens. Stellar OFTs integrate with SEP-41 tokens — Stellar’s standard token interface (analogous to ERC-20 on EVM).Classic Assets Receiving Requirements
These requirements apply when your OFT token is a SAC (wrapped classic asset). If you are using a custom contract token, trustlines are not required for any address type.G-Address (EOA)
G-address recipients must meet two prerequisites before they can receive classic assets:- Account activation: The account must hold a minimum of 1 XLM to exist on the Stellar network.
- Trustline: The account must have an explicit trustline for the classic asset being received.
lz_receive fails due to unmet prerequisites, delivery can be retried once the recipient account is activated and the trustline is established.
C-Address (Smart Contract)
C-address recipients are not subject to these restrictions. As long as the contract address exists on-chain, it can receive assets directly.OFT Types
Stellar uses a single OFT contract with anOftType enum to determine behavior:
Setup & Deployment
Prerequisites:- Stellar CLI (v25.1.0+)
- Rust (v1.90.0+) with
wasm32v1-nonetarget (rustup target add wasm32v1-none) - Familiarity with Soroban smart contracts and Rust on Soroban
- Token (SAC): Your SEP-41 token
- SAC Manager: RBAC-controlled admin wrapper that implements
Mintable. The OFT calls it to mint; it calls the SAC as its admin. - OFT: Deployed with
oft_type: MintBurn(sac_manager_address)
Step 1: Create and Deploy Your Token
If you don’t already have a token, deploy a SEP-41-compliant fungible token on Soroban. The simplest approach is to wrap a Stellar classic asset as a Stellar Asset Contract (SAC):Step 2: Deploy the SAC Manager (Mintable Contract)
For MintBurn mode, the OFT needs a contract that implements theMintable trait to mint tokens on crosschain receive:
Cargo.toml with LayerZero dependencies:
The SAC Manager source is available in the LayerZero Stellar contracts. You can also implement your own contract — any contract that satisfies the
Mintable trait will work.Step 3: Create and Deploy the OFT Contract
Create OFT project:Cargo.toml with LayerZero dependencies:
Check the OFT core, OApp contracts, protocol contracts on LayerZero GitHub for the latest Stellar contract packages.
Build and deploy:
Unlike the OApp constructor (which takes separate
owner and delegate parameters), the OFT constructor uses delegate as both the initial owner and the endpoint delegate.Step 4: Grant Minting Permissions
Grant theMINTER_ROLE on the SAC Manager to the OFT contract, so it can mint tokens during crosschain receives:
Step 5: Configure the OFT
Theset_peer and set_enforced_options functions require the contract’s AUTHORIZER (the owner by default). After deployment, configure peers and enforced options:
Using LockUnlock Mode
If you don’t have mint authority over the token (e.g., bridging an existing asset), use LockUnlock mode instead. The OFT holds tokens in its own balance — locking on send, unlocking on receive. No SAC Manager or Mintable contract is needed. Simply deploy the OFT withLockUnlock:
Core Operations
Sending Tokens
Sending tokens crosschain involves quoting the fee and executing the transfer:Step 1: Build SendParam
Step 2: Quote the Transfer
quote_oft returns:
Both
quote_oft and quote_send are read-only — they do not execute any state changes.
Step 3: Execute Send
from.require_auth() is called internally).
Receiving Tokens
Token receipt is handled automatically by the OFT contract’slz_receive implementation:
- Decode the
OFTMessagefrom the inbound payload - Resolve the recipient address — the crosschain OFT message carries only the 32-byte address payload, but Stellar addresses are 35 bytes (1-byte version + 32-byte payload + 2-byte checksum) with two possible types (C-address for contracts, G-address for accounts). The
resolve_addressutility disambiguates by checking if a contract exists at the 32-byte address; if so, it resolves to a C-address, otherwise it falls back to a G-address. This is handled automatically. - Convert amount from shared decimals to local decimals
- Credit tokens:
- LockUnlock:
token.transfer(from: oft_contract, to: recipient, amount) - MintBurn:
mintable.mint(to: recipient, amount, operator: oft_contract)
- LockUnlock:
- If a compose message is included, register it via
endpoint.send_composefor subsequent execution - Emit
OFTReceivedevent
Decimal Precision
OFTs use a shared decimal system for crosschain consistency. The shared decimals value determines the precision used in the crosschain message, while local decimals are the token’s actual precision.
Recommended configuration:
OFT Extensions
The Stellar OFT supports three optional extensions that can be enabled at deployment:Pausable
Pause and unpause all OFT operations (send, receive, quote):send, quote_send, quote_oft, and lz_receive (credit) all revert with error code 3110 (Paused).
OFT Fee
Charge a fee on outbound transfers, configurable per destination or as a default:fee = amount_ld * fee_bps / 10_000
Rate Limiter
Control transfer volume with a leaky bucket algorithm:OFT Wire Format
For developers building custom integrations, the OFT message wire format:- Message type 1 (
SEND):send_to+amount_sd - Message type 2 (
SEND_AND_CALL):send_to+amount_sd+compose_from+compose_msg
Events
Best Practices & Deployment Checklist
- Set
shared_decimalscarefully — it cannot be changed after deployment. Use 6 for compatibility with most chains. - Configure peers on both sides — the remote OFT must also set your Stellar OFT as a peer.
- Set enforced options — always configure minimum gas for each destination to prevent failed deliveries.
- Test with small amounts — verify the full send/receive flow with minimal amounts before large transfers.
- Monitor rate limits — if enabled, ensure limits are set appropriately for expected volume.
- Grant minting permissions — for MintBurn mode, ensure the OFT has the required role on the Mintable contract.
Next Steps
- DVN & Executor Config: Configure your security stack.
- Technical Overview: Understand the full message lifecycle.
- Troubleshooting: Common errors and solutions.