Docs

payment_queryFeeDetails - Enjin RPC Method

Get a detailed fee breakdown for an extrinsic on Enjin. Analyze base fee, length fee, and adjusted weight fee components to optimize transaction costs for high-volume NFT minting (2,000+ per tx), gaming assets, and cross-chain NFT transfers via Paratoken standard.

Returns a detailed breakdown of the inclusion fee for a given extrinsic on Enjin. While payment_queryInfo returns the total fee as a single value, this method separates it into three components: the fixed base fee, the length-proportional fee, and the weight-based adjusted fee. This granularity is essential for understanding and optimizing transaction costs.

If you provide blockHash, it must be a real chain block hash. Placeholder hashes and stale examples return an unknown Block style error.

Why Enjin? Build on the purpose-built NFT blockchain with protocol-level minting and $100M Metaverse Fund with NFT functions at protocol level, Fuel Tanks for subsidized fees, 700-1000 TPS, 6-second finality, and ERC-1155 standard pioneer.

When to Use This Method

payment_queryFeeDetails is essential for game developers, NFT creators, and enterprises building cross-chain digital assets:

  • Fee Optimization -- Identify which fee component dominates your transaction cost and optimize accordingly on Enjin
  • Transaction Cost Analysis -- Build detailed cost breakdowns for high-volume NFT minting (2,000+ per tx), gaming assets, and cross-chain NFT transfers via Paratoken standard, showing users exactly where their fees go
  • Fee Model Comparison -- Compare fee structures across different extrinsic types or between runtime upgrades that change fee parameters
  • Batching Decisions -- Determine whether batching calls saves fees by amortizing the base fee across multiple operations

Code Examples

Bash
# Query fee details for an encoded extrinsic
curl -X POST https://enjin-matrix-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryFeeDetails",
    "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
    "id": 1
  }'

# Query fee details at a specific block
# Replace 0xYOUR_RECENT_BLOCK_HASH with a recent finalized hash from chain_getFinalizedHead
curl -X POST https://enjin-matrix-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryFeeDetails",
    "params": [
      "0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01...",
      "0xYOUR_RECENT_BLOCK_HASH"
    ],
    "id": 1
  }'

Common Use Cases

1. Fee Component Analysis for Optimization

Analyze which fee component dominates to guide optimization strategies:

JavaScript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function analyzeFeeComponents(api, tx) {
  const feeDetails = await api.rpc.payment.queryFeeDetails(tx.toHex());

  if (feeDetails.inclusionFee.isNone) {
    return { feeless: true };
  }

  const fee = feeDetails.inclusionFee.unwrap();
  const base = BigInt(fee.baseFee.toString());
  const len = BigInt(fee.lenFee.toString());
  const weight = BigInt(fee.adjustedWeightFee.toString());
  const total = base + len + weight;

  const analysis = {
    baseFee: { value: base, percentage: Number((base * 10000n) / total) / 100 },
    lenFee: { value: len, percentage: Number((len * 10000n) / total) / 100 },
    weightFee: { value: weight, percentage: Number((weight * 10000n) / total) / 100 },
    total
  };

  // Suggest optimization based on dominant component
  if (analysis.lenFee.percentage > 50) {
    analysis.suggestion = 'Length fee dominates -- reduce call data size or batch smaller calls';
  } else if (analysis.weightFee.percentage > 50) {
    analysis.suggestion = 'Weight fee dominates -- choose lighter runtime operations';
  } else {
    analysis.suggestion = 'Fees are balanced -- batch calls to amortize base fee';
  }

  return analysis;
}

2. Batch vs. Individual Fee Comparison

Compare the cost of batching calls versus submitting them individually:

JavaScript
async function compareBatchVsIndividual(api, calls) {
  // Individual fee total
  let individualTotal = 0n;
  for (const call of calls) {
    const tx = api.tx(call);
    const details = await api.rpc.payment.queryFeeDetails(tx.toHex());
    if (details.inclusionFee.isSome) {
      const fee = details.inclusionFee.unwrap();
      individualTotal += BigInt(fee.baseFee.toString())
        + BigInt(fee.lenFee.toString())
        + BigInt(fee.adjustedWeightFee.toString());
    }
  }

  // Batched fee
  const batchTx = api.tx.utility.batchAll(calls);
  const batchDetails = await api.rpc.payment.queryFeeDetails(batchTx.toHex());
  let batchTotal = 0n;
  if (batchDetails.inclusionFee.isSome) {
    const fee = batchDetails.inclusionFee.unwrap();
    batchTotal = BigInt(fee.baseFee.toString())
      + BigInt(fee.lenFee.toString())
      + BigInt(fee.adjustedWeightFee.toString());
  }

  const savings = individualTotal - batchTotal;
  console.log(`Individual total: ${individualTotal} planck`);
  console.log(`Batch total:      ${batchTotal} planck`);
  console.log(`Savings:          ${savings} planck (${Number((savings * 10000n) / individualTotal) / 100}%)`);

  return { individualTotal, batchTotal, savings };
}

3. Fee Tracking Across Runtime Upgrades

Monitor how fee components change after runtime upgrades to detect regressions:

JavaScript
async function compareFeesBetweenBlocks(api, extrinsicHex, blockHashBefore, blockHashAfter) {
  const [before, after] = await Promise.all([
    api.rpc.payment.queryFeeDetails(extrinsicHex, blockHashBefore),
    api.rpc.payment.queryFeeDetails(extrinsicHex, blockHashAfter)
  ]);

  function extractFees(details) {
    if (details.inclusionFee.isNone) return null;
    const fee = details.inclusionFee.unwrap();
    return {
      base: BigInt(fee.baseFee.toString()),
      len: BigInt(fee.lenFee.toString()),
      weight: BigInt(fee.adjustedWeightFee.toString())
    };
  }

  const feesBefore = extractFees(before);
  const feesAfter = extractFees(after);

  if (feesBefore && feesAfter) {
    console.log('Fee comparison:');
    console.log(`  Base fee:   ${feesBefore.base} -> ${feesAfter.base}`);
    console.log(`  Length fee: ${feesBefore.len} -> ${feesAfter.len}`);
    console.log(`  Weight fee: ${feesBefore.weight} -> ${feesAfter.weight}`);
  }
}

Fee Components Explained

ComponentSourceHow It's CalculatedOptimization Strategy
baseFeeExtrinsicBaseWeightFixed cost per extrinsic defined by the runtimeBatch multiple calls into a single extrinsic to pay only one base fee
lenFeeTransactionByteFeeencodedLength × lengthToFee coefficientMinimize encoded extrinsic size by using compact encodings and avoiding large payloads
adjustedWeightFeeWeightToFeeExecution weight multiplied by the fee multiplier, which adjusts based on block fullnessChoose lighter operations, submit during low-traffic periods when the multiplier is lower

Tip multiplier: The adjustedWeightFee is sensitive to network congestion. When blocks are consistently more than half full, the fee multiplier increases, raising the weight fee. During low-traffic periods, the multiplier decreases toward its minimum.

Error Handling

Common errors and solutions:

Error CodeDescriptionSolution
-32602Invalid paramsEnsure the extrinsic hex string is properly SCALE-encoded with the 0x prefix
-32603Internal errorThe runtime could not compute fees -- verify the extrinsic is valid for the target block
-32005Rate limit exceededReduce request frequency or implement client-side rate limiting
Null inclusionFeeFeeless extrinsicInherent extrinsics and some unsigned extrinsics do not pay fees -- this is expected behavior