Paying Fees
This guide walks you through paying transaction fees on Aztec using various payment methods.
Prerequisites
-
Connected to a network with an
EmbeddedWalletinstance and funded accounts - Understanding of fee concepts
The fee asset is only transferrable within a block to the current sequencer, as it powers the fee abstraction mechanism on Aztec. The asset is not transferable beyond this to ensure credible neutrality between all third party developer made asset portals and to ensure local compliance rules can be followed.
Payment methods overview
| Method | Use Case | Privacy | Requirements |
|---|---|---|---|
| Fee Juice (default) | Account already has Fee Juice | Public | Funded account |
| Sponsored FPC | Testing, free transactions | Public | None |
| Private FPC | Privacy-preserving fees | Private | Bridged Fee Juice via FPC |
| Third-party FPC | Pay in other tokens on testnet/mainnet | Varies by FPC | FPC provider's SDK |
| Bridge + Claim | Bootstrap from L1 | Public | L1 ETH for gas |
Mana and Fee Juice
Mana is Aztec's unit of computational effort (like gas on Ethereum), and Fee Juice is the native fee token used to pay for transactions. For a detailed explanation of these concepts, see Fee Concepts.
Estimate mana costs
When using EmbeddedWallet, gas is estimated automatically on every send() call. You only need to manually estimate if you want to preview costs before sending, or if you're using a custom wallet implementation.
Before sending a transaction, you can estimate the mana it will consume by simulating with estimateGas: true:
const { estimatedGas } = await token.methods
.transfer_in_public(aliceAddress, bobAddress, 1n, 0n)
.simulate({
from: aliceAddress,
fee: { estimateGas: true, estimatedGasPadding: 0.1 },
});
Source code: docs/examples/ts/aztecjs_advanced/index.ts#L343-L350
The estimatedGas object contains:
gasLimits.daGas- Estimated DA mana for main executiongasLimits.l2Gas- Estimated L2 mana for main executionteardownGasLimits.daGas- Estimated DA mana for teardown phaseteardownGasLimits.l2Gas- Estimated L2 mana for teardown phase
Calculate expected fee from estimate
To calculate the expected fee from estimated gas, use the computeFee method with current network fees:
const currentFees = await node.getCurrentMinFees();
const estimatedFee = estimatedGas.gasLimits.computeFee(currentFees).toBigInt();
console.log("Estimated fee:", estimatedFee);
Source code: docs/examples/ts/aztecjs_advanced/index.ts#L352-L356
The estimatedGasPadding parameter adds a safety margin to the estimate. A value of 0.1 adds 10% padding. Use higher padding for transactions with variable gas costs.
Get transaction fee from receipt
After a transaction is mined, you can retrieve the fee paid from the receipt:
const { receipt: feeReceipt } = await token.methods
.mint_to_public(aliceAddress, 1n)
.send({ from: aliceAddress });
console.log("Transaction fee:", feeReceipt.transactionFee);
Source code: docs/examples/ts/aztecjs_advanced/index.ts#L358-L363
The transactionFee field is a bigint representing the total fee paid in the fee token (Fee Juice). You can also check execution status:
console.log("Succeeded:", feeReceipt.hasExecutionSucceeded());
console.log("Block:", feeReceipt.blockNumber);
console.log("Fee paid:", feeReceipt.transactionFee);
Source code: docs/examples/ts/aztecjs_advanced/index.ts#L365-L369
Pay with Fee Juice
Fee Juice is the native fee token on Aztec.
If your account has Fee Juice (for example, from a faucet), is deployed, and is registered in your wallet, it will be used automatically to pay for the fee of the transaction:
// contract is a deployed contract instance; aliceAddress is from the connection guide
const { receipt: feeJuiceReceipt } = await token.methods
.mint_to_public(aliceAddress, 1n)
.send({
from: aliceAddress,
// no fee payment method needed — Fee Juice is used automatically
});
console.log("Transaction fee:", feeJuiceReceipt.transactionFee);
Source code: docs/examples/ts/aztecjs_advanced/index.ts#L371-L380
Use Fee Payment Contracts
Fee Payment Contracts (FPCs) pay Fee Juice on your behalf. An FPC holds its own Fee Juice balance to pay the protocol and can accept other tokens from users in exchange. Some FPCs operate privately by design, routing fee payments through private notes rather than public function calls.
The SDK includes PrivateFeePaymentMethod and PublicFeePaymentMethod classes for the built-in reference FPC, but these are deprecated and do not work on mainnet alpha. For custom-token fee payment, use a third-party FPC with its own SDK (see below).
Sponsored FPC
The Sponsored FPC is not available on mainnet. It is available on testnet, devnet, and local network.
The Sponsored FPC pays fees unconditionally, enabling free transactions. It is available on testnet, devnet, and local network.
You can derive the Sponsored FPC address from its deployment parameters, register it with your wallet, and use it to pay for transactions:
// Set up the Sponsored FPC (see fees guide for full setup)
const sponsoredFPCInstance = await getContractInstanceFromInstantiationParams(
SponsoredFPCContract.artifact,
{ salt: new Fr(0) },
);
await wallet.registerContract(sponsoredFPCInstance, SponsoredFPCContract.artifact);
const sponsoredPaymentMethod = new SponsoredFeePaymentMethod(sponsoredFPCInstance.address);
// wallet is from the connection guide; sponsoredPaymentMethod is from the fees guide
const { contract: sponsoredContract } = await TokenContract.deploy(
wallet,
aliceAddress,
"SponsoredToken",
"SPT",
18,
).send({ from: aliceAddress, fee: { paymentMethod: sponsoredPaymentMethod } });
Source code: docs/examples/ts/aztecjs_advanced/index.ts#L42-L59
Here's a simpler example from the test suite:
const bananasToSendToBob = 10n;
const { receipt: tx } = await bananaCoin.methods
.transfer_in_public(aliceAddress, bobAddress, bananasToSendToBob, 0)
.send({
from: aliceAddress,
fee: {
gasSettings,
paymentMethod: new SponsoredFeePaymentMethod(sponsoredFPC.address),
},
});
Source code: yarn-project/end-to-end/src/e2e_fees/sponsored_payments.test.ts#L57-L68
Private Fee Payment
For transactions where the fee payment itself should be private, you can use a fully private FPC — one that holds Fee Juice claimed from L1 as an internal private balance, works on every network, and never needs an onchain deployment. See Pay Fees Privately for how this pattern works and a walkthrough using a community-built example.
When multiple apps derive the same private FPC address (using the same artifact and salt), every private fee payment joins a single, larger privacy set. See Recommended salt for details.
Third-party FPCs on testnet and mainnet
On networks where the Sponsored FPC is unavailable, third-party FPCs deployed by ecosystem teams let you pay fees in tokens other than Fee Juice. Each FPC provider typically offers an SDK or API that handles payment method construction on the client side — this may include quote fetching and authwit creation, though the exact flow depends on the FPC design. For background on how FPCs work at the protocol level, see How FPCs work.
Example: Nethermind Private Multi Asset FPC
To illustrate how a third-party FPC integration works, the following walkthrough uses Nethermind's Private Multi Asset FPC as a reference. This is one implementation — other FPCs may differ in design and API.
This FPC is quote-based and operates privately:
- A single deployment accepts many tokens — the asset is selected per quote rather than hard-coded at deploy time.
- Fee payments are transferred as private notes, so fee activity is not visible onchain.
- An operator-run attestation service signs per-user quotes binding the FPC address, accepted asset, amounts, expiry, and user.
- A cold-start entrypoint allows a brand-new account to bridge tokens from L1, claim on L2, and pay the fee in a single transaction. Note that the cold-start path calls
Token::mint_to_private, which enqueues a public call to update the token's total supply — so the minted amount is visible onchain even though the user's identity and balances remain private.
This FPC is developed and maintained by Nethermind, not by Aztec Labs. The SDK (@nethermindeth/aztec-fpc-sdk) may not yet be published to npm — check the repository README for current install instructions. Review the protocol spec and evaluate independently before integrating.
The SDK wraps the quote-and-pay flow into a single call. The snippet below shows the general shape of the integration (illustrative — verify against the current SDK API before using):
import { FpcClient } from "@nethermindeth/aztec-fpc-sdk";
// Point the client at the FPC's attestation service
const fpcClient = new FpcClient({
fpcAddress, // the deployed FPC contract address
operator, // operator's Aztec address
node, // PXE or node connection
attestationBaseUrl: "https://...", // attestation service URL from the FPC provider
});
// Estimate gas, fetch a signed quote, and build the payment method
const payment = await fpcClient.createPaymentMethod({
wallet,
user: aliceAddress, // the account paying the fee
tokenAddress, // the token you want to pay in
estimatedGas, // from a prior estimateGas call
});
// Use it like any other payment method
const tx = await myContract.methods.myMethod(args).send({ fee: payment.fee });
await tx.wait();
For the cold-start flow, deployment addresses, and the full API, see the aztec-fpc repository.
Bridge Fee Juice from L1
Fee Juice is non-transferable on L2, but you can bridge it from L1, claim it on L2, and use it. This involves a few components that are part of a running network's infrastructure:
- An L1 fee juice contract
- An L1 fee juice portal
- An L2 fee juice portal
- An L2 fee juice contract
aztec.js provides helpers to simplify the process:
import { createExtendedL1Client } from "@aztec/ethereum/client";
import { L1FeeJuicePortalManager } from "@aztec/aztec.js/ethereum";
import { createLogger } from "@aztec/aztec.js/log";
// Create an L1 client (accepts a mnemonic or 0x-prefixed private key)
const l1RpcUrl = process.env.ETHEREUM_HOST ?? "http://localhost:8545";
const l1Mnemonic =
"test test test test test test test test test test test junk";
const l1Client = createExtendedL1Client([l1RpcUrl], l1Mnemonic);
// Create a portal manager to interact with the L1 fee juice portal
const logger = createLogger("docs:fee-juice-bridge");
const portalManager = await L1FeeJuicePortalManager.new(node, l1Client, logger);
Source code: docs/examples/ts/aztecjs_connection/index.ts#L92-L106
Under the hood, L1FeeJuicePortalManager gets the L1 addresses from the node node_getNodeInfo endpoint. It then exposes an easy method bridgeTokensPublic which mints fee juice on L1 and sends it to an L2 address via the L1 portal:
// portalManager is from the L1FeeJuicePortalManager setup above
// feeJuiceAccount.address is an Aztec address from createSchnorrAccount
const claim = await portalManager.bridgeTokensPublic(
feeJuiceAccount.address, // the L2 address
1000000000000000000000n, // the amount to send to the L1 portal
true, // whether to mint or not (set to false if your L1 account already has fee juice!)
);
console.log("Claim secret:", claim.claimSecret);
console.log("Claim amount:", claim.claimAmount);
Source code: docs/examples/ts/aztecjs_connection/index.ts#L108-L119
After this transaction is minted on L1 and a few blocks pass, you can claim the message on L2 and use it directly to pay for fees:
import { FeeJuicePaymentMethodWithClaim } from "@aztec/aztec.js/fee";
// claim is from the bridgeTokensPublic step above
// Create a payment method that claims the bridged Fee Juice and uses it to pay
const bridgePaymentMethod = new FeeJuicePaymentMethodWithClaim(feeJuiceAccount.address, claim);
// Use it to pay for any transaction — here we deploy the account in one step
const deployMethodBridged = await feeJuiceAccount.getDeployMethod();
await deployMethodBridged.send({
from: NO_FROM,
fee: { paymentMethod: bridgePaymentMethod },
});
Source code: docs/examples/ts/aztecjs_connection/index.ts#L152-L165
Configure gas settings
Understanding gas dimensions
Gas settings specify limits and fees for both DA and L2 dimensions:
- gasLimits: Maximum mana for main execution phase
- teardownGasLimits: Maximum mana for teardown phase (used by FPCs for refunds)
- maxFeesPerGas: Maximum price you're willing to pay per mana unit
- maxPriorityFeesPerGas: Priority fee for faster inclusion
The fee limit is calculated as gasLimits × maxFeesPerGas for each dimension.
Set custom gas limits
Set custom gas limits by importing from stdlib:
// Query current network fees to set realistic limits
const networkFees = await node.getCurrentMinFees();
const gasSettings = GasSettings.from({
gasLimits: { daGas: 100_000, l2Gas: 2_000_000 },
teardownGasLimits: { daGas: 100_000, l2Gas: 2_000_000 },
maxFeesPerGas: { feePerDaGas: networkFees.feePerDaGas * 2n, feePerL2Gas: networkFees.feePerL2Gas * 2n },
maxPriorityFeesPerGas: { feePerDaGas: 0n, feePerL2Gas: 0n },
});
Source code: docs/examples/ts/aztecjs_advanced/index.ts#L382-L391
Then pass the settings when sending:
const { receipt: gsReceipt } = await token.methods.mint_to_public(aliceAddress, 1n).send({
from: aliceAddress,
fee: { gasSettings },
});
Source code: docs/examples/ts/aztecjs_advanced/index.ts#L393-L398
Note that gasLimits and teardownGasLimits use daGas/l2Gas field names, while maxFeesPerGas and maxPriorityFeesPerGas use feePerDaGas/feePerL2Gas.
Use automatic gas estimation
When using EmbeddedWallet, gas estimation happens automatically on every send() — you don't need to pass estimateGas. This option is useful for custom wallet implementations or when you want to estimate gas during a simulate() call.
// Estimate gas for a transaction before sending
const { estimatedGas: autoEstimate } = await token.methods
.mint_to_public(aliceAddress, 1n)
.simulate({
from: aliceAddress,
fee: {
estimateGas: true,
estimatedGasPadding: 0.2, // 20% padding
},
});
console.log("Auto-estimated L2 gas:", autoEstimate.gasLimits.l2Gas);
Source code: docs/examples/ts/aztecjs_advanced/index.ts#L408-L420
Gas estimation runs a simulation first to determine actual gas usage, then adds padding for safety. This works with all payment methods, including FPCs.
Next steps
- Learn about fee concepts in detail
- Explore authentication witnesses for delegated payments
- See testing guide for fee testing strategies