Agent documentation index: llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Skip to main content

OFT Fees

Overview​

A bridge out of Flare or Coston2 charges two separate fees. See FXRP OFT Adapter for the contracts, and OFT deployments for deployment addresses.

FeePaid inWhat it pays forHow it is set
Token feeFXRP, taken from the amount you sendThe Flare system fee on the bridged tokensStored on the OFT contract, in basis points
Message feeNative gas token of the source chain (FLR or C2FLR)LayerZero delivery of the cross-chain messageQuoted at send time from the executor and the DVNs

The token fee reduces the amount of FXRP that arrives at the destination. The message fee is attached as msg.value on send and does not reduce the FXRP amount.

Both fees are read from the OFT Adapter on the source chain.

ChainAdapter
Flare0xd70659a6396285BF7214d7Ea9673184e7C72E07E
Coston20xCd3d2127935Ae82Af54Fc31cCD9D3440dbF46639

The message fee below is for a plain transfer: no compose message, and extraOptions left empty so the adapter uses its on-chain enforced options. A compose path, such as a send that continues to HyperCore, asks for more destination gas and quotes a higher message fee.

Quote again before sending

The token fee is a stored rate. The message fee and the DVN fee move with destination gas prices and DVN price feeds. Call quoteSend in the same transaction flow as send, and pass the returned nativeFee as msg.value.

Token Fee​

The token fee is a basis-point cut of the amount debited on the source chain. BPS_DENOMINATOR is 10_000, so 100 is 1%.

The adapter stores two rates:

  • defaultFeeBps() is the rate used for every destination.
  • feeBps(dstEid) returns FeeConfig { uint16 feeBps; bool enabled; }. When enabled is true, that destination uses feeBps instead of the default. A stored feeBps of 0 with enabled == true is an explicit zero for that destination.

getFee(dstEid, amountLD) returns the token fee for an amount, in token units (FXRP has 6 decimals). quoteOFT applies the same calculation and reports the difference between amount sent and amount received as one line item, Flare system fee.

Message Fee​

quoteSend(sendParam, payInLzToken) returns MessagingFee { uint256 nativeFee; uint256 lzTokenFee; }.

Pass payInLzToken = false. These pathways quote lzTokenFee = 0, and nativeFee is the value to send with send.

nativeFee is the sum of three worker quotes inside the LayerZero send library (SendUln302):

ComponentWho is paidHow it is quoted
Executor feeThe LayerZero executor, which calls lzReceive on the destinationILayerZeroExecutor.getFee
DVN feeEach required DVN, which verifies the messageILayerZeroDVN.getFee, summed
Treasury feeThe LayerZero treasuryILayerZeroTreasury.getFee

On the pathways quoted below, the treasury fee was 0, so nativeFee = executor fee + DVN fee.

A plain OFT payload is 40 bytes: a 32-byte recipient and an 8-byte amount in shared decimals. FXRP uses 6 decimals on every chain, matching the shared decimals, so the payload size doesn't change with the amount. Quoting 1 FXRP and quoting 1,000 FXRP returns the same message fee for a plain transfer.

Flare Mainnet​

Every wired route uses 20 source confirmations and 130,000 gas for lzReceive. The required DVNs are LayerZero Labs, Nethermind, Canary, and Horizen. Amounts are in FLR, rounded to 0.01.

DestinationEIDExecutorDVNMessage fee
HyperEVM30367104.290.52104.81
Ethereum30101218.6216.44235.06
Base30184208.530.93209.47
BNB Smart Chain30102174.522.20176.73
Monad30390208.130.16208.28
Katana3037534.810.2735.08

HyperCore is not a LayerZero endpoint. A send there is a compose message on the HyperEVM route above, so this plain-transfer quote does not include it.

DVN fee by provider, in FLR:

DestinationLayerZero LabsNethermindCanaryHorizen
HyperEVM0.130.130.130.13
Ethereum4.104.064.194.10
Base0.230.230.240.23
BNB Smart Chain0.550.540.560.55
Monad0.040.040.040.04
Katana0.070.070.070.07

Flare DVN addresses, also listed on the OFT overview:

DVNAddress
LayerZero Labs0x9c061c9a4782294eef65ef28cb88233a987f4bdd
Nethermind0x9bcd17a654bffaa6f8fea38d19661a7210e22196
Canary0xd791948db16ab4373fa394b74c727ddb7fb02520
Horizen0xeaa5a170d2588f84773f965281f8611d61312832

Coston2 Testnet​

Every quoted route uses 2 source confirmations and 100,000 gas for lzReceive. One DVN is required: LayerZero Labs at 0x12523de19dc41c91f7d2093e0cfbb76b17012c8d

. Amounts are in C2FLR, rounded to 0.01.

DestinationEIDExecutorDVNMessage fee
Ethereum Sepolia4016121.861.0922.95
Hyperliquid Testnet4036221.921.1323.05

Retrieve the Token Fee​

quoteOFT is the call to use in an integration. It returns the rate limit, the fee line items, and the amount that will arrive. Use the verified FAssetOFTAdapter ABI.

import { createPublicClient, http, pad, parseUnits } from "viem";
import { flare } from "viem/chains";

const OFT_ADAPTER = "0xd70659a6396285BF7214d7Ea9673184e7C72E07E";
const ETHEREUM_EID = 30101;

const publicClient = createPublicClient({
chain: flare,
transport: http("https://flare-api.flare.network/ext/C/rpc"),
});

const amountLD = parseUnits("1", 6); // FXRP has 6 decimals

const sendParam = {
dstEid: ETHEREUM_EID,
to: pad("0x1111111111111111111111111111111111111111", { size: 32 }),
amountLD,
minAmountLD: amountLD,
extraOptions: "0x",
composeMsg: "0x",
oftCmd: "0x",
} as const;

const [oftLimit, oftFeeDetails, oftReceipt] = await publicClient.readContract({
address: OFT_ADAPTER,
abi: oftAbi,
functionName: "quoteOFT",
args: [sendParam],
});

// oftReceipt.amountSentLD tokens debited on Flare
// oftReceipt.amountReceivedLD tokens minted on the destination
// oftFeeDetails[0].description === "Flare system fee" when a fee is charged
// oftFeeDetails[0].feeAmountLD is the token fee, in 6-decimal units

To read the configured rate without an amount:

const defaultFeeBps = await publicClient.readContract({
address: OFT_ADAPTER,
abi: oftAbi,
functionName: "defaultFeeBps",
});

const feeConfig = await publicClient.readContract({
address: OFT_ADAPTER,
abi: oftAbi,
functionName: "feeBps",
args: [ETHEREUM_EID],
});
// feeConfig.feeBps is used only when feeConfig.enabled is true.
// Otherwise, the destination uses defaultFeeBps.

const tokenFee = await publicClient.readContract({
address: OFT_ADAPTER,
abi: oftAbi,
functionName: "getFee",
args: [ETHEREUM_EID, amountLD],
});

The same calls on Coston2 use adapter 0xCd3d2127935Ae82Af54Fc31cCD9D3440dbF46639 and RPC https://coston2-api.flare.network/ext/C/rpc.

Retrieve the message fee​

quoteSend is the call to use before send. An empty extraOptions is valid on a route that enforces options: the adapter merges those options into the quote.

const { nativeFee, lzTokenFee } = await publicClient.readContract({
address: OFT_ADAPTER,
abi: oftAbi,
functionName: "quoteSend",
args: [sendParam, false], // false = pay in native gas, not lzToken
});

// Pass nativeFee as msg.value:
// oft.send(sendParam, { nativeFee, lzTokenFee }, refundAddress, { value: nativeFee })

nativeFee is the full message fee for that sendParam, including the DVN fee. You pay that value once. The DVN fee is already inside it.

What's next