Estimate and pay gas
Estimate the gas fee for a message and pay it to the gas service.
Paying gas takes two steps:
- Estimate the fee for the message, with the AxelarJS SDK or the Axelarscan API.
- Pay it to the gas service on the source chain, in the same transaction and before calling the gateway (pay gas).
Both need the execution gas limit: the gas your destination contract's _execute uses. Measure it on a testnet or a fork, and add some margin.
Estimate the fee
Estimate the fee off-chain, right before sending the transaction, since gas prices change.
With the AxelarJS SDK
estimateGasFee on AxelarQueryAPI returns the total fee in the source chain's smallest unit (wei on EVM chains):
import { AxelarQueryAPI, Environment } from '@axelar-network/axelarjs-sdk';
const api = new AxelarQueryAPI({ environment: Environment.MAINNET });
const fee = await api.estimateGasFee(
'ethereum', // source chain
'avalanche', // destination chain
200000, // execution gas limit
);Optional parameters help messages that can't afford to get stuck:
gasMultipliermultiplies the execution fee, e.g.1.2for a 20% buffer. The default,"auto", uses Axelar's multiplier for the chain.minGasPricesets a floor, in wei, for the destination gas price. Useful on chains where gas prices spike.executeDatais the destination transaction's calldata. For rollup destinations it makes the L1 data fee more accurate; without it, a placeholder is used.gmpParamswithshowDetailedFees: truereturns the breakdown instead of a total. Its type also requiresdestinationContractAddress,sourceContractAddressandtokenSymbol; pass empty strings for a plain message, e.g.{ showDetailedFees: true, destinationContractAddress: '', sourceContractAddress: '', tokenSymbol: '' }.
See AxelarJS SDK for every parameter.
With the Axelarscan API
Without the SDK, call the same estimate over HTTP:
curl -X POST https://api.axelarscan.io/gmp/estimateGasFee \
-H 'content-type: application/json' \
-d '{"sourceChain":"ethereum","destinationChain":"avalanche","gasLimit":"200000"}'It returns the total fee in wei. Add "showDetailedFees": true for the breakdown, and "gasMultiplier" or "minGasPrice" as with the SDK. For testnet, use https://testnet.api.axelarscan.io.
The detailed response includes totalFee, its parts (baseFee, executionFeeWithMultiplier, expressFee) and isExpressSupported, which tells you whether Express is available between the two chains. For rollup destinations, gasLimitWithL1Fee is the gas limit with the L1 data fee included.
Estimate ITS fees
An Interchain Token Service message travels in two hops, from the source chain to the ITS Hub on Axelar, then to the destination chain, and one payment on the source chain covers both. Estimate it with estimateITSFee, which returns the fee for both hops, in the source chain's smallest unit:
curl -X POST https://api.axelarscan.io/gmp/estimateITSFee \
-H 'content-type: application/json' \
-d '{"sourceChain":"ethereum","destinationChain":"Avalanche","event":"InterchainTransfer"}'Set event to the kind of message you're sending; it defaults to a transfer:
| Message | event | destinationChain |
|---|---|---|
| A transfer | InterchainTransfer | The destination |
| Deploying an interchain or canonical token to another chain | InterchainTokenDeployment | The destination |
| Linking a custom token | LinkToken | The destination |
| Registering token metadata with the ITS Hub | TokenMetadataRegistered | axelar |
showDetailedFees, gasMultiplier and minGasPrice work as for estimateGasFee; the detailed response lists each hop under details. gasLimit sets the gas limit of the execution on the destination chain, in place of the default for the event: set it for a transfer that calls a contract with data, after measuring what that call uses.
For testnet, use https://testnet.api.axelarscan.io. The AxelarJS SDK has the same estimate, estimateITSFee on AxelarQueryAPI, without gasLimit. Its type for event doesn't include InterchainTokenDeployment or TokenMetadataRegistered yet, so estimate those with the API.
Pay the fee to ITS, not to the gas service: ITS pays the gas service itself. Each chain's ITS integration shows how.
Pay gas
Call the gas service, then the gateway, in the same transaction. The payment is matched to the message by sender, destination chain, destination address and payload, so pass the same values to both:
import { AxelarExecutable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol';
import { IAxelarGasService } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol';
contract Sender is AxelarExecutable {
IAxelarGasService public immutable gasService;
constructor(address gateway_, address gasService_) AxelarExecutable(gateway_) {
gasService = IAxelarGasService(gasService_);
}
function sendMessage(
string calldata destinationChain,
string calldata destinationAddress,
string calldata message
) external payable {
bytes memory payload = abi.encode(message);
// msg.value is the fee from estimateGasFee
gasService.payNativeGasForContractCall{ value: msg.value }(
address(this), // sender: the contract that calls the gateway
destinationChain,
destinationAddress,
payload,
msg.sender // refund address
);
gateway().callContract(destinationChain, destinationAddress, payload);
}
function _execute(bytes32, string calldata, string calldata, bytes calldata) internal override {}
}From your frontend, send the estimate as the transaction value:
await sender.sendMessage('avalanche', '0x...', 'hello', { value: fee });Payment functions
All are on IAxelarGasService. The Native functions take the native token as msg.value; the others take an ERC-20 gasToken and gasFeeAmount, and need an approval first.
| Message | Native token | ERC-20 |
|---|---|---|
callContract | payNativeGasForContractCall | payGasForContractCall |
callContractWithToken (consensus chains only) | payNativeGasForContractCallWithToken | payGasForContractCallWithToken |
| Express | payNativeGasForExpressCall | payGasForExpressCall |
| Express with token | payNativeGasForExpressCallWithToken | payGasForExpressCallWithToken |