> ## Documentation Index
> Fetch the complete documentation index at: https://docs.layerzero.network/llms.txt
> Use this file to discover all available pages before exploring further.

# OFT and OApp Alt Variants on Tempo

> Why Tempo requires OFTAlt and OAppAlt instead of standard contracts, and how the ERC-20 fee path works with EndpointV2Alt.

## Why Alt variants on Tempo

Tempo has no native gas token. Sending `msg.value > 0` reverts, which means the standard fee payment path (fees via `msg.value` to the endpoint) does not work.

Tempo uses [EndpointV2Alt](/v2/concepts/protocol/layerzero-endpoint-alt) instead of the standard `EndpointV2`. EndpointV2Alt replaces native token fee payment with ERC-20 token transfers using [LZEndpointDollar (LZD)](/v2/developers/tempo/reference/lz-endpoint-dollar). OFTAlt and OAppAlt are the contract variants designed to work with this ERC-20 fee path.

<Info>
  **Building an OApp?** `OAppAlt` changes only the fee payment path: `_payNative` transfers LZD to the endpoint instead of using `msg.value`. The receive side is identical to `OAppReceiver`. Everything else (messaging, peer configuration, security) works the same way. Import from `@layerzerolabs/oapp-alt-evm/contracts/oapp/OAppAlt.sol`.
</Info>

## How standard OFT fee payment works

On a typical EVM chain, OFT pays LayerZero messaging fees by attaching native value:

```solidity wrap theme={null}
// Standard OFT on Ethereum, Arbitrum, etc.
MessagingFee memory fee = oft.quoteSend(sendParam, false);
oft.send{value: fee.nativeFee}(sendParam, fee, refundAddress);
```

The endpoint's `_payNative` function reads `msg.value` and forwards native tokens to the message library. The endpoint refunds excess native value to the sender.

## EndpointV2Alt and the ERC-20 fee path

On Tempo, `EndpointV2Alt` overrides `_payNative` to delegate to `_payToken`, which:

1. **Rejects native value**: reverts with `LZ_OnlyAltToken` if `msg.value > 0`
2. **Reads ERC-20 balance**: `_suppliedNative()` returns `IERC20(nativeErc20).balanceOf(address(this))` instead of `msg.value`
3. **Exposes the fee token**: `nativeToken()` returns the LZD address instead of `address(0)`

OFTAlt contracts use this ERC-20 fee path. Before calling `send()`, the caller must approve and transfer LZD to the endpoint.

## Comparison table

| Aspect          | OFT                                        | OFTAlt                                            | OFTAdapter                                        | OFTAdapterAlt                                            |
| --------------- | ------------------------------------------ | ------------------------------------------------- | ------------------------------------------------- | -------------------------------------------------------- |
| **Fee payment** | `msg.value` (native)                       | ERC-20 `transferFrom` (LZD)                       | `msg.value` (native)                              | ERC-20 `transferFrom` (LZD)                              |
| **Endpoint**    | EndpointV2                                 | EndpointV2Alt                                     | EndpointV2                                        | EndpointV2Alt                                            |
| **msg.value**   | required for fees                          | must be 0                                         | required for fees                                 | must be 0                                                |
| **Native drop** | supported                                  | not supported                                     | supported                                         | not supported                                            |
| **Token model** | new omnichain token                        | new omnichain token                               | wraps existing token                              | wraps existing token                                     |
| **Import path** | `@layerzerolabs/oft-evm/contracts/OFT.sol` | `@layerzerolabs/oft-alt-evm/contracts/OFTAlt.sol` | `@layerzerolabs/oft-evm/contracts/OFTAdapter.sol` | `@layerzerolabs/oft-alt-evm/contracts/OFTAdapterAlt.sol` |

## Contract interface

OFTAlt extends the standard OFT interface but targets EndpointV2Alt:

```solidity wrap theme={null}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import { OFTAlt } from "@layerzerolabs/oft-alt-evm/contracts/OFTAlt.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

contract MyOFTAlt is OFTAlt {
    constructor(
        string memory _name,
        string memory _symbol,
        address _lzEndpoint, // EndpointV2Alt address on Tempo
        address _delegate
    ) OFTAlt(_name, _symbol, _lzEndpoint, _delegate) Ownable(_delegate) {}
}
```

For adapting an existing ERC-20 token on Tempo, use `OFTAdapterAlt`. This uses lock/unlock: it locks tokens in the adapter on send and unlocks on receive.

```solidity wrap theme={null}
import { OFTAdapterAlt } from "@layerzerolabs/oft-alt-evm/contracts/OFTAdapterAlt.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

contract MyOFTAdapterAlt is OFTAdapterAlt {
    constructor(
        address _token,        // existing ERC-20 on Tempo
        address _lzEndpoint,   // EndpointV2Alt address
        address _delegate
    ) OFTAdapterAlt(_token, _lzEndpoint, _delegate) Ownable(_delegate) {}
}
```

<Warning>
  **Do not use `OFTAdapterAlt` for TIP-20 tokens.** TIP-20 tokens only support `burn(amount)` (burning from `msg.sender`), not `burn(from, amount)`. Use [`OFTBurnSelfMintAlt`](/v2/developers/tempo/reference/tip-20-token-standard#integration-with-oftalt) instead — it transfers tokens to itself first, then burns from the adapter address.
</Warning>

## Fee payment differences

### Standard OFT (other chains)

```solidity wrap theme={null}
// Fees paid via msg.value
MessagingFee memory fee = oft.quoteSend(sendParam, false);
oft.send{value: fee.nativeFee}(sendParam, fee, refundAddress);
```

### OFTAlt (Tempo)

```solidity wrap theme={null}
// Fees paid via ERC-20 transfer
MessagingFee memory fee = oft.quoteSend(sendParam, false);

// Approve the OFT to spend LZD for the fee
IERC20(lzd).approve(address(oft), fee.nativeFee);

// Send with msg.value = 0
oft.send{value: 0}(sendParam, fee, refundAddress);
```

<Warning>
  On Tempo, you must wrap a whitelisted stablecoin into LZD and approve the OFT
  **before** calling `send()`. See the [LZD
  reference](/v2/developers/tempo/reference/lz-endpoint-dollar) for the wrap
  flow.
</Warning>

## Interoperability

OFTAlt on Tempo communicates with standard OFT deployments on other chains without issue. The LayerZero protocol handles translation between fee models:

* **Sending from Tempo**: the sender pays fees in LZD via OFTAlt. The destination chain receives the message as usual.
* **Receiving on Tempo**: the sender on the source chain pays fees in the source chain's native token using standard OFT. The Tempo-side OFTAlt receives the message without requiring LZD from the receiver.

No changes are needed to existing OFT contracts on other chains. The cross-chain message format is the same.

## Choosing the right contract

| Scenario                                       | Tempo contract       | Other chains          |
| ---------------------------------------------- | -------------------- | --------------------- |
| New ERC-20, minted on Tempo                    | `OFTAlt`             | `OFT`                 |
| Existing ERC-20 on Tempo, lock/unlock bridging | `OFTAdapterAlt`      | `OFT` or `OFTAdapter` |
| Existing TIP-20 on Tempo, mint/burn bridging   | `OFTBurnSelfMintAlt` | `OFT` or `OFTAdapter` |

**OFTAlt** is for new omnichain tokens where Tempo is a mint chain. It deploys a fresh ERC-20 on Tempo and handles cross-chain mint/burn.

**OFTAdapterAlt** wraps an existing ERC-20 that already lives on Tempo. It locks tokens in the adapter on send and unlocks on receive. This is the Alt equivalent of [OFTAdapter](/v2/developers/evm/oft/quickstart#oft-adapter), targeting EndpointV2Alt instead of EndpointV2.

**OFTBurnSelfMintAlt** is the [`OFTBurnSelfMint`](/v2/developers/evm/stablecoin-oft/ofts#oftburnselfmint) variant for chains using `EndpointV2Alt`. It bridges [TIP-20 tokens](/v2/developers/tempo/reference/tip-20-token-standard) using a transfer-then-burn pattern: on send, it transfers tokens to itself first, then burns from the adapter address. This is required because TIP-20 tokens only support `burn(amount)` (burning from `msg.sender`), not `burn(from, amount)`. On receive, it mints tokens directly to the recipient. The contract must hold `ISSUER_ROLE` on the TIP-20 token.

On other chains, keep using standard OFT or OFTAdapter. No changes needed. The Tempo-side adapter connects to EndpointV2Alt while the other chains continue using EndpointV2. LayerZero routes messages between them without extra configuration.
