Skip to content

Hydra transaction builder requires explicit fee: 0 in request, does not default to protocol parameters #70

@Arpit-K-Sharma

Description

@Arpit-K-Sharma

Summary

When building transactions via the /hydra/tx?submit=false endpoint using the official kuber-client npm package (against the devnet provided in this repository), the backend requires clients to explicitly set fee: 0 in transaction requests. Without this explicit parameter, transactions fail with a balance error—even though the devnet protocol parameters define zero transaction fees (txFeeFixed: 0, txFeePerByte: 0).

Environment

  • Package: kuber-client (npm)
  • Endpoint: /hydra/tx?submit=false
  • Providers:
    hydra0 = new KuberHydraApiProvider("http://localhost:8082");
    hydra1 = new KuberHydraApiProvider("http://localhost:8083");
    hydra2 = new KuberHydraApiProvider("http://localhost:8084");
    providers = [hydra0, hydra1, hydra2];
  • Devnet Protocol Parameters:
    • txFeeFixed: 0
    • txFeePerByte: 0
    • executionUnitPrices.priceMemory: 0
    • executionUnitPrices.priceSteps: 0
  • Devnet Setup:
    • Using /kuber-hydra/devnet/runtime/protocol-parameters.json as protocol parameters
    • Hydra node started via provided devnet scripts

Steps to Reproduce

  1. Start a Hydra Head node using the devnet setup and protocol parameters with zero fees:
    {
      "txFeeFixed": 0,
      "txFeePerByte": 0,
      "executionUnitPrices": {
        "priceMemory": 0,
        "priceSteps": 0
      }
    }
  2. Query protocol parameters to verify zero fees:
    curl http://localhost:8082/hydra/query/protocol-parameters
  3. Attempt to build a transaction without explicitly setting fee: 0:
    const tx = await provider.buildTx({
        inputs: ["<txHash>#<index>"],
        outputs: [{
            address: "addr_test1...",
            value: { lovelace: 5000000 }
        }],
        changeAddress: "addr_test1..."
        // Note: fee parameter is omitted
    });
  4. Observe the error:
    APIError: Api Error response [500] : {
      "message": "Missing Balance :[(AdaAssetId,400000)]",
      "type": "InsufficientInput"
    }
    
  5. Add explicit fee: 0 to the request:
    const tx = await provider.buildTx({
        inputs: ["<txHash>#<index>"],
        outputs: [{
            address: "addr_test1...",
            value: { lovelace: 5000000 }
        }],
        changeAddress: "addr_test1...",
        fee: 0  // ← Explicitly set to zero
    });
  6. Transaction now builds successfully.

Expected Behavior

The transaction builder should automatically derive the fee from the active protocol parameters when the fee field is omitted from the request. Since the Hydra Head protocol parameters explicitly define txFeeFixed: 0 and txFeePerByte: 0, the builder should:

  1. Query the protocol parameters if not already cached
  2. Apply the zero-fee policy from those parameters
  3. Balance the transaction accordingly without requiring client intervention

This behavior would be consistent with standard Cardano transaction building semantics, where protocol parameters govern fee calculation by default.

Actual Behavior

When the fee field is omitted, the backend appears to calculate or assume a non-zero fee (approximately 400,000 lovelace based on the error message), leading to:

  • Transaction imbalance errors
  • Failed transaction construction
  • Requirement for clients to manually specify fee: 0

This behavior is problematic because:

  • It contradicts the protocol parameter configuration
  • It breaks the abstraction layer that protocol parameters are supposed to provide
  • It forces all clients to have explicit knowledge of Hydra's zero-fee model
  • It creates inconsistent behavior between Hydra and mainnet transaction builders

Impact

Developer Experience:

  • Requires workarounds in client code that shouldn't be necessary
  • Creates confusion about whether fees apply in Hydra Heads
  • Breaks intuitive API usage patterns

Code Maintenance:

  • Forces boilerplate fee: 0 in every transaction request
  • Makes code less portable between Hydra and mainnet contexts
  • Complicates automated testing and CI/CD pipelines

Example of Required Workaround:

// Current workaround needed in every transaction
function buildHydraTx(provider, inputs, outputs, changeAddress) {
    return provider.buildTx({
        inputs,
        outputs,
        fee: 0,  // Must explicitly set despite protocol params
        changeAddress
    });
}

Proposed Solution

Modify the transaction builder backend logic to:

  1. Default to protocol parameters: When fee is not provided in the request, query the current protocol parameters and calculate the fee accordingly

  2. Respect zero-fee protocols: If txFeeFixed and txFeePerByte are both zero, automatically set transaction fee to zero

  3. Maintain backward compatibility: Continue to respect explicitly provided fee values to allow manual overrides when necessary

Image

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions