# payment_queryFeeDetails - Bifrost RPC Method

Returns a detailed breakdown of the inclusion fee for a given extrinsic on Bifrost. 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 Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`payment_queryFeeDetails` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Fee Optimization** -- Identify which fee component dominates your transaction cost and optimize accordingly on Bifrost
- **Transaction Cost Analysis** -- Build detailed cost breakdowns for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging, 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

## Best Practices

- Returns `baseFee`, `lenFee`, and `adjustedWeightFee` for detailed cost analysis
- More granular than `payment_queryInfo` -- useful for gas optimization
- Fee components are calculated from weight and length of the extrinsic
- Weight-adjusted fees may vary based on current network congestion

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-serialized extrinsic (signed or unsigned)
- `blockHash` (`String, optional`): Block hash at which to calculate fees; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "payment_queryFeeDetails",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `inclusionFee` (`Option<InclusionFee>, required`): Fee breakdown object, or null if the extrinsic does not pay fees
- `baseFee` (`String, required`): Fixed fee charged per extrinsic regardless of size or complexity (human-readable decimal string)
- `lenFee` (`String, required`): Fee proportional to the encoded byte length of the extrinsic (length * lengthToFee)
- `adjustedWeightFee` (`String, required`): Fee based on execution weight, adjusted by the current block fullness multiplier

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "inclusionFee": {
      "baseFee": "124414000000",
      "lenFee": "1430000000",
      "adjustedWeightFee": "2183055836"
    }
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: Could not decode extrinsic"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# Query fee details for an encoded extrinsic
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -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://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryFeeDetails",
    "params": [
      "0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01...",
      "0xYOUR_RECENT_BLOCK_HASH"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Create a sample transfer extrinsic
const tx = api.tx.balances.transferKeepAlive(
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
  1000000000000n
);

// Get fee details
const feeDetails = await api.rpc.payment.queryFeeDetails(tx.toHex());

if (feeDetails.inclusionFee.isSome) {
  const fee = feeDetails.inclusionFee.unwrap();
  console.log('Base fee:', fee.baseFee.toString());
  console.log('Length fee:', fee.lenFee.toString());
  console.log('Weight fee:', fee.adjustedWeightFee.toString());

  const total = fee.baseFee.add(fee.lenFee).add(fee.adjustedWeightFee);
  console.log('Total inclusion fee:', total.toString());
}

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'payment_queryFeeDetails',
    params: ['0x2d028400...signedExtrinsicHex'],
    id: 1
  })
});

const { result } = await response.json();
if (result.inclusionFee) {
  console.log('Fee components:', result.inclusionFee);
}
```

```python
import requests

def query_fee_details(extrinsic_hex, block_hash=None):
    params = [extrinsic_hex]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'payment_queryFeeDetails',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query fee details for an encoded extrinsic
encoded_extrinsic = '0x2d028400...'
result = query_fee_details(encoded_extrinsic)

if result['inclusionFee']:
    fee = result['inclusionFee']
    base = int(fee['baseFee'])
    length = int(fee['lenFee'])
    weight = int(fee['adjustedWeightFee'])
    total = base + length + weight

    print(f"Base fee:   {base:>20} planck")
    print(f"Length fee: {length:>20} planck")
    print(f"Weight fee: {weight:>20} planck")
    print(f"Total:      {total:>20} planck")
else:
    print('Extrinsic does not pay fees')

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('payment_queryFeeDetails', [encoded_extrinsic])['result']
print(f"Fee details: {result}")
```

```rust
use serde_json::json;

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct FeeDetailsResponse {
    inclusion_fee: Option<InclusionFee>,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct InclusionFee {
    base_fee: String,
    len_fee: String,
    adjusted_weight_fee: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let extrinsic_hex = "0x2d028400...";

    let response = client
        .post("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "payment_queryFeeDetails",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let body: serde_json::Value = response.json().await?;
    let details: FeeDetailsResponse = serde_json::from_value(body["result"].clone())?;

    match details.inclusion_fee {
        Some(fee) => {
            let base: u128 = fee.base_fee.parse()?;
            let len: u128 = fee.len_fee.parse()?;
            let weight: u128 = fee.adjusted_weight_fee.parse()?;
            let total = base + len + weight;

            println!("Base fee:   {:>20}", base);
            println!("Length fee: {:>20}", len);
            println!("Weight fee: {:>20}", weight);
            println!("Total:      {:>20}", total);
        }
        None => println!("Extrinsic does not pay fees"),
    }

    Ok(())
}
```

## 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

| Component             | Source                | How It's Calculated                                                                      | Optimization Strategy                                                                     |
| --------------------- | --------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| **baseFee**           | `ExtrinsicBaseWeight` | Fixed cost per extrinsic defined by the runtime                                          | Batch multiple calls into a single extrinsic to pay only one base fee                     |
| **lenFee**            | `TransactionByteFee`  | `encodedLength × lengthToFee` coefficient                                                | Minimize encoded extrinsic size by using compact encodings and avoiding large payloads    |
| **adjustedWeightFee** | `WeightToFee`         | Execution weight multiplied by the fee multiplier, which adjusts based on block fullness | Choose 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.

## Related Methods

- [`payment_queryInfo`](https://www.dwellir.com/docs/bifrost/payment_queryInfo) -- Get the total fee and execution weight for an extrinsic as a single value
- [`state_call`](https://www.dwellir.com/docs/bifrost/state_call) -- Call `TransactionPaymentApi_query_fee_details` directly for more control
- [`system_properties`](https://www.dwellir.com/docs/bifrost/system_properties) -- Get token decimals and symbol for human-readable fee display
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bifrost/author_submitExtrinsic) -- Submit the extrinsic after confirming acceptable fees
- [`author_submitAndWatchExtrinsic`](https://www.dwellir.com/docs/bifrost/author_submitAndWatchExtrinsic) -- Submit and track the extrinsic through finalization
