Yes. Source-chain contracts remain unchanged, but the Tempo side requires Alt contract variants and pays fees in LZD instead of msg.value. The message format, pathway configuration, and security model are the same.
Source-chain contracts may need updated enforcedOptions to account for Tempo’s higher gas costs. See what changes on Tempo below.
Fees are paid in LZD instead of msg.value (this is because Tempo has no native gas token). You must wrap a stablecoin into LZD and approve the OFT before calling send{value: 0}()
Native drop is not supported (addExecutorNativeDropOption reverts)
Gas costs are higher on Tempo for certain operations (state creation, new accounts). Set custom enforcedOptions with higher gas limits for lzReceive when Tempo is the destination. See TIP-1010 for the full gas schedule
// 1. Quote the fee (view call)MessagingFee memory fee = oft.quoteSend(sendParam, false);// 2. Approve the asset token to the OFT (bridge amount)IERC20(usdce).approve(address(oft), sendParam.amountLD);// 3. Approve the fee token to LZD (fee amount)IERC20(usdce).approve(address(lzd), fee.nativeFee);// 4. Wrap stablecoin into LZD (token must be whitelisted by LZD)lzd.wrap(usdce, msg.sender, fee.nativeFee);// 5. Approve LZD to the OFT (fee amount)IERC20(lzd).approve(address(oft), fee.nativeFee);// 6. Send with msg.value = 0oft.send{value: 0}(sendParam, fee, refundAddress);
The TempoOFTWrapper simplifies this by handling wrapping, approvals, and sending in a single transaction:
// 1. Quote the feeMessagingFee memory fee = oft.quoteSend(sendParam, false);// 2. Approve the fee token to the wrapper (bridge amount + messaging fee)IERC20(usdce).approve(address(wrapper), sendParam.amountLD + fee.nativeFee);// 3. Call sendOFT: wrapper handles wrap, approve, and send atomicallywrapper.sendOFT( address(oft), // OFT contract usdce, // fee token (whitelisted stablecoin) sendParam, // standard send parameters fee.nativeFee // max acceptable fee (reverts if re-quote exceeds this));
The wrapper:
Pulls tokens from the caller:
Same token (e.g., USDC.e for both bridging and fees): pulls amountLD + nativeFee in a single transfer
Different tokens (e.g., bridging EURC.e, fees in USDC.e): pulls amountLD and nativeFee separately, requiring two approvals
Wraps the fee portion into LZD
Approves LZD to the OFT
Calls oft.send() with the correct fee
Reverts the entire transaction if the re-quoted fee exceeds maxNativeFee
You can preflight with an eth_call to sendOFT to obtain
oftReceipt.amountSentLD before sending the live transaction, and avoid dust.
If the bridged token differs from the fee token (e.g., bridging EURC.e but paying fees in USDC.e), you will need to approve the asset token and fee token to the wrapper separately before calling sendOFT.
The Stargate frontend uses LZMulticall to bundle wrapping, approvals, and sending into a single transaction. On Tempo, it bundles the LZD wrap and approval steps alongside the OFT send.
LZD is not available on DEXes. LZD is an ERC-20, not a TIP-20 token, so it cannot be traded on the Tempo DEX. Always use LZD.wrap() to obtain fee tokens.
Wrapper becomes msg.sender. When using the TempoOFTWrapper, the wrapper is the sender for the OFT call. Do not use it for compose messages where the composer refunds the original sender. The refund goes to the wrapper, not the user, and the funds will be permanently lost.
Fee token must be a whitelisted stablecoin, not LZD. The feeToken passed to the wrapper must be a 6-decimal stablecoin whitelisted by LZD (pathUSD, USDC.e, or USDT0). Passing LZD itself reverts.