Skip to main content
Version: Endpoint V1

Estimating Message Fee

Get the quantity of native gas token to pay to send a message

Call estimateFees() to return a tuple containing the cross chain message fee.

info

There are 2 values returned as a tuple via estimateFees(). Use the 0th index to get the fee in wei to pass as value to Endpoint.send()

You do not need to implement this function. This is just to show how the fee is calculated by the endpoint for the send() function.

The estimateFees() function returns a dynamic fee based on Oracle and Relayer prices for the destination chainId, your UserApplication contract, and payload parameters.

In solidity, you can use the ILayerZeroEndpoint.sol interface to call the view function to get the send() fees.

Endpoint estimateFees()

// Endpoint.sol estimateFees() returns the fees for the message
function estimateFees(uint16 _dstChainId, address _userApplication, bytes calldata _payload, bool _payInZRO, bytes calldata _adapterParams) external view override returns (uint nativeFee, uint zroFee) {
LibraryConfig storage uaConfig = uaConfigLookup[_userApplication];
ILayerZeroMessagingLibrary lib = uaConfig.sendVersion == DEFAULT_VERSION ? defaultSendLibrary : uaConfig.sendLibrary;
return lib.estimateFees(_dstChainId, _userApplication, _payload, _payInZRO, _adapterParams);
}

Our implementation of lib.estimateFees() illustrates how the total fee is calculated, which is the cumulative amount the oracle and relayer are collecting plus, potentially, a small protocol fee.

// full estimateFees implementation
function estimateFees(uint16 _chainId, address _ua, bytes calldata _payload, bool _payInZRO, bytes calldata _adapterParams) external view override returns (uint nativeFee, uint zroFee) {
uint16 chainId = _chainId;
address ua = _ua;
uint payloadSize = _payload.length;
bytes memory adapterParam = _adapterParams;

ApplicationConfiguration memory uaConfig = getAppConfig(chainId, ua);

// Relayer Fee
uint relayerFee;
{
if (adapterParam.length == 0) {
bytes memory defaultAdapterParam = defaultAdapterParams[chainId][uaConfig.outboundProofType];
relayerFee = ILayerZeroRelayer(uaConfig.relayer).getPrice(chainId, uaConfig.outboundProofType, ua, payloadSize, defaultAdapterParam);
} else {
relayerFee = ILayerZeroRelayer(uaConfig.relayer).getPrice(chainId, uaConfig.outboundProofType, ua, payloadSize, adapterParam);
}
}

// Oracle Fee
uint oracleFee = ILayerZeroOracle(uaConfig.oracle).getPrice(chainId, uaConfig.outboundProofType);

// LayerZero Fee
{
uint protocolFee = treasuryContract.getFees(_payInZRO, relayerFee, oracleFee);
_payInZRO ? zroFee = protocolFee : nativeFee = protocolFee;
}

// return the sum of fees
nativeFee = nativeFee.add(relayerFee).add(oracleFee);
}

Offchain Fee Estimation Example

const fees = await endpoint.estimateFees(
dstChainId, // the destination LayerZero chainId
uaContractAddress, // your contract address that calls Endpoint.send()
"0x", // empty payload
false, // _payInZRO
"0x" // default '0x' adapterParams, see: Relayer Adapter Param docs
)
console.log(`fees[0] is the message fee in wei: ${fees[0]}`);
info

Check out adapterParams to customize the gas amount or airdrop native ETH!

AdapterParams shows how to pack some additional settings to be used by estimateFees() and send() - it instructs LayerZero to use more gas which may be necessary to not run into a StoredPayload.