Skip to main content
The Omnichain Fungible Token (OFT) Standard can be extended to support several different use cases, similar to the ERC20 token standard. In addition to the OApp Design Patterns and Extensions, the following examples demonstrate how to modify your OFT contract for specific use cases.

Composed OFT

A composed message refers to an OApp that invokes the LayerZero Endpoint method sendCompose to deliver a composed call to another contract on the destination chain via lzCompose. Because OFT inherits the base OApp implementation, you can also send composed messages within your OFT receive logic. Diagram showing OFT composed message flow: Source OFT sends tokens with a composeMsg, Destination OFT handles token delivery via lzReceive, then calls sendCompose to trigger the Composed Receiver contract via lzCompose If you are not familiar with how OApp Composing works, review that section first before continuing here.

Composing an OFT

The OFT Standard comes pre-packaged with methods for delivering composed calls to the destination OFT contract for handling.
  1. Source OFT: The Source OFT specifies in the send call a composed message in bytes for delivering to. You can think of this the same as how _lzSend sends arbitrary bytes to a destination, which the destination contract uses in the _lzReceive business logic.
  2. Destination OFT(s): When the send call is received by the destination OFT, the internal _lzReceive function in OFTCore.sol handles the delivery of tokens along with the composed call.
  3. Composed Receiver(s): the contract interface implementing business logic to handle receiving a composed message via lzCompose.

Sending Token

When sending a token from source to destination, the caller has the option to specify an additional composeMsg in bytes.
Depending on your implementation, this composed message field can be used to pass any arbitrary information as bytes along with your token to the destination address.

Composed Message Execution Options

You will need to pass both an lzReceiveOption and lzComposeOption as either your enforced or extra options for this call to succeed. You can decide both the _gas and msg.value that should be used for the composed call(s), depending on the type and quantity of messages you intend to send. Your configured Executor will use the _options provided in the original _lzSend call to determine the gas limit and amount of msg.value to include per message _index:
It’s important to remember that gas values may vary depending on the destination chain. For example, all new Ethereum transactions cost 21000 wei, but other chains may have lower or higher opcode costs, or entirely different gas mechanisms. You can read more about generating _options and the role of _index in Message Execution Options.

Sending Compose

By default, the destination OFT’s _lzReceive method will check if the message is composed, and then deliver those arbitrary bytes to the specified toAddress:
As shown in the sendCompose comments, the base OFT implementation only allows for 1 composed message per lzReceive call. To add additional composed calls, you will need to override the _lzReceive method and add custom composed logic.

Receiving Compose

The receiving address of the crosschain token transfer will need to implement custom business logic to handle the composed message, for example, consider this mock contract that swaps an inbound OFT for an ERC20:
You will need to use the OFTComposeMsgCodec to extract the composeMsg and _amountLD from the overall message, before decoding it.
The above example enforces that the _amountLD was deposited to this contract!The OFT Standard will only credit tokens and call sendCompose to the _toAddress provided on the source chain:

Further Reading

OFT Alt

When deploying OApps, you might encounter scenarios where the native gas token cannot be used to pay the LayerZero Endpoint to send a message. These Endpoints have been deployed using the EndpointV2Alt.sol contract, so that they can use an alternative ERC20 token on the same chain to pay for crosschain messages. Because these Endpoints do not use the native gas token, some changes must be made to your OApp contracts (including OFT). For example, the OFTAlt.sol demonstrates this implementation fully, which you can reference when modifying your other OApp-based contracts:

Contract Changes

At a high level, only a few changes to your OApp are needed to interact with the EndpointV2Alt.sol contract:

Pass OpenZeppelin Ownable

Make sure to pass OpenZeppelin’s Ownable modifier to the constructor. The access control has already been applied, but must be explicitly passed in the constructor to compile successfully.

Using SafeERC20

You should include the SafeERC20 library for safely interacting with ERC20 tokens. This is crucial for ensuring that token transfers handle potential errors like reverts or exceptions.

Error Handling

A custom error LzAltTokenUnavailable which is used to handle cases where the native ERC20 token for fee payment is not set in the EndpointV2Alt contract.

Override _payNative

You should override the _payNative function to handle paying using an ERC20 token. This function checks if the ERC20 token address is set (nativeErc20), reverts if not, and performs a safeTransferFrom to transfer the fee from the sender to the endpoint. This ensures that the contract can handle fees in the specified ERC20 token by the EndpointV2Alt.

Override _lzSend

To apply the changes made in _payNative, you should also override _lzSend to handle the ERC20 token fee.
Because _lzSend now uses an ERC20 token as payment, you must now approve the OFT as a spender of your ERC20 token.