Mesh LogoMesh

Hydra Provider (beta)

Build high-throughput applications on Cardano's Layer 2 scaling solution

Overview

The Hydra Head protocol is a Layer 2 scaling solution for Cardano rooted in peer-reviewed research. It increases transaction throughput and ensures cost efficiency while maintaining rigorous security.

Key features:

  • High transaction throughput with near-instant finality
  • Low transaction costs within a Hydra Head
  • Full Cardano smart contract compatibility
  • Peer-reviewed cryptographic security

Use Hydra when you need:

  • Real-time transaction settlement
  • High-frequency trading or gaming applications
  • Micropayments without Layer 1 fees
  • Private transaction channels between parties

Quick Start

Install the MeshJS Hydra package:

npm install @meshsdk/hydra

Initialize the provider:

import { HydraProvider } from "@meshsdk/hydra";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

Connect to a Hydra Head and send transactions:

// Connect to the Hydra Head
await provider.connect();

// Listen for messages
provider.onMessage((message) => {
  console.log("Hydra message:", message);
});

// Submit a transaction
const txHash = await provider.submitTx(signedTx);

Configuration Options

ParameterTypeRequiredDescription
httpUrlstringYesThe Hydra node HTTP API URL

Hydra Node Setup

Before using the HydraProvider, you need a running Hydra node connected to a Hydra Head. See the Hydra Getting Started Guide for node setup instructions.

API Reference

connect

Establish a WebSocket connection to the Hydra Head.

await provider.connect(): Promise<void>

Example:

import { HydraProvider } from "@meshsdk/hydra";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

await provider.connect();
console.log("Connected to Hydra Head");

disconnect

Close the connection to the Hydra Head.

await provider.disconnect(timeout?: number): Promise<void>
ParameterTypeRequiredDescription
timeoutnumberNoTimeout in milliseconds (default: 300000 / 5 minutes)

Example:

import { HydraProvider } from "@meshsdk/hydra";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

await provider.connect();

// ... do work ...

// Disconnect with default timeout (5 minutes)
await provider.disconnect();

// Or disconnect with custom timeout (10 minutes)
await provider.disconnect(10 * 60 * 1000);

onMessage

Listen for messages from the Hydra Head. Use this to receive state updates, transaction confirmations, and other events.

provider.onMessage(callback: (message: HydraMessage) => void): void
ParameterTypeRequiredDescription
callback(message: HydraMessage) => voidYesFunction called when a message is received

Example:

import { HydraProvider } from "@meshsdk/hydra";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

await provider.connect();

provider.onMessage((message) => {
  console.log("Message received:", message);

  if (message.tag === "Greetings") {
    console.log("Snapshot UTxOs:", message.snapshotUtxo);
  }

  if (message.tag === "TxValid") {
    console.log("Transaction confirmed:", message.transaction);
  }
});

See Hydra API Reference for all event types.


Hydra Head Commands

init

Initialize a new Hydra Head. This is a no-op if a Head is already open.

await provider.init(): Promise<void>

Example:

import { HydraProvider } from "@meshsdk/hydra";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

await provider.connect();
await provider.init();

console.log("Hydra Head initialized");

abort

Abort a Head before it is opened. This can only be done before all participants have committed. Once opened, use close instead.

await provider.abort(): Promise<void>

Example:

import { HydraProvider } from "@meshsdk/hydra";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

await provider.connect();
await provider.abort();

console.log("Hydra Head aborted");

newTx

Submit a transaction to the Hydra Head. The transaction is broadcast only if well-formed and valid.

await provider.newTx(tx: HydraTransaction): Promise<string>
ParameterTypeRequiredDescription
tx.cborHexstringYesThe transaction CBOR in hex format
tx.typestringYesTransaction type (e.g., "Tx ConwayEra")
tx.descriptionstringNoOptional description

Returns: The transaction hash.

Example:

import { HydraProvider, MeshTxBuilder } from "@meshsdk/hydra";
import { MeshCardanoHeadlessWallet, AddressType } from "@meshsdk/wallet";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

await provider.connect();

// Build a transaction
const wallet = await MeshCardanoHeadlessWallet.fromCliKeys({
  networkId: 0,
  walletAddressType: AddressType.Enterprise,
  fetcher: provider,
  submitter: provider,
  paymentSkey: "58201aae63d93899640e91b51c5e8bd542262df3ecf3246c3854f39c40f4eb83557d",
});

const utxos = await wallet.getUtxosMesh();
const changeAddress = await wallet.getChangeAddressBech32();

const txBuilder = new MeshTxBuilder({
  fetcher: provider,
  params: await provider.fetchProtocolParameters(),
});

const unsignedTx = await txBuilder
  .txOut("addr_test1vpd5axpq4qsh8sxvzny49cp22gc5tqx0djf6wmjv5cx7q5qyrzuw8", [
    { unit: "lovelace", quantity: "3000000" },
  ])
  .changeAddress(changeAddress)
  .selectUtxosFrom(utxos)
  .complete();

// Submit via newTx
const txHash = await provider.newTx({
  cborHex: unsignedTx,
  type: "Tx ConwayEra",
  description: "Payment transaction",
});

console.log("Transaction submitted:", txHash);

decommit

Request to decommit a UTxO from the Head. Upon reaching consensus, the corresponding outputs become available on Layer 1.

await provider.decommit(tx: HydraTransaction): Promise<void>
ParameterTypeRequiredDescription
tx.cborHexstringYesThe decommit transaction CBOR in hex format
tx.typestringYesTransaction type (e.g., "Tx ConwayEra")
tx.descriptionstringNoOptional description

Example:

import { HydraProvider } from "@meshsdk/hydra";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

await provider.connect();

await provider.decommit({
  cborHex: "<decommit_tx_cbor>",
  type: "Tx ConwayEra",
  description: "Decommit funds to L1",
});

close

Close the Head with the latest known snapshot. This moves the Head from Open to Close state and begins the contestation period. No more transactions can be submitted after closing.

await provider.close(): Promise<void>

Example:

import { HydraProvider } from "@meshsdk/hydra";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

await provider.connect();
await provider.close();

console.log("Hydra Head closed - contestation period started");

contest

Challenge the latest snapshot announced during head closure. Participants can only contest once.

await provider.contest(): Promise<void>

Example:

import { HydraProvider } from "@meshsdk/hydra";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

await provider.connect();
await provider.contest();

console.log("Snapshot contested");

fanout

Finalize Head UTxOs to Layer 1 after the contestation period has passed.

await provider.fanout(): Promise<void>

Example:

import { HydraProvider } from "@meshsdk/hydra";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

await provider.connect();
await provider.fanout();

console.log("Funds returned to Layer 1");

Query Methods

fetchAddressUTxOs

Fetch UTxOs controlled by an address within the Hydra Head.

await provider.fetchAddressUTxOs(address: string, asset?: string): Promise<UTxO[]>
ParameterTypeRequiredDescription
addressstringYesThe wallet address
assetstringNoFilter by asset unit

Returns: Array of UTxO objects.

Example:

import { HydraProvider } from "@meshsdk/hydra";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

await provider.connect();

const utxos = await provider.fetchAddressUTxOs(
  "addr_test1vpsthwvxgfkkm2lm8ggy0c5345u6vrfctmug6tdyx4rf4mqn2xcyw"
);

console.log(`Found ${utxos.length} UTxOs`);

fetchUTxOs

Fetch UTxOs by transaction hash within the Hydra Head.

await provider.fetchUTxOs(hash: string, index?: number): Promise<UTxO[]>
ParameterTypeRequiredDescription
hashstringYesThe transaction hash
indexnumberNoSpecific output index

Returns: Array of UTxO objects.

Example:

import { HydraProvider } from "@meshsdk/hydra";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

await provider.connect();

const utxos = await provider.fetchUTxOs("txHash");

fetchAssetAddresses

Fetch addresses and quantities for a given asset within the Hydra Head.

await provider.fetchAssetAddresses(asset: string): Promise<AssetAddress[]>
ParameterTypeRequiredDescription
assetstringYesThe asset unit (policy ID + asset name hex)

Returns: Array of addresses and quantities.

Example:

import { HydraProvider } from "@meshsdk/hydra";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

await provider.connect();

const holders = await provider.fetchAssetAddresses(
  "d9312da562da182b02322fd8acb536f37eb9d29fba7c49dc172555274d657368546f6b656e"
);

fetchCollectionAssets

Fetch assets for a given policy ID within the Hydra Head.

await provider.fetchCollectionAssets(policyId: string): Promise<Asset[]>
ParameterTypeRequiredDescription
policyIdstringYesThe policy ID

Returns: Array of assets.

Example:

import { HydraProvider } from "@meshsdk/hydra";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

await provider.connect();

const assets = await provider.fetchCollectionAssets(
  "d9312da562da182b02322fd8acb536f37eb9d29fba7c49dc17255527"
);

fetchProtocolParameters

Fetch protocol parameters for the Hydra Head.

await provider.fetchProtocolParameters(epoch?: number): Promise<Protocol>
ParameterTypeRequiredDescription
epochnumberNoNot used in Hydra context

Returns: Protocol object with Hydra Head parameters.

Example:

import { HydraProvider } from "@meshsdk/hydra";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

await provider.connect();

const params = await provider.fetchProtocolParameters();
console.log("Protocol parameters:", params);

submitTx

Submit a signed transaction to the Hydra Head.

await provider.submitTx(tx: string): Promise<string>
ParameterTypeRequiredDescription
txstringYesThe signed transaction CBOR hex

Returns: The transaction hash.

Example:

import { HydraProvider } from "@meshsdk/hydra";

const provider = new HydraProvider({
  httpUrl: "http://123.45.67.890:4001",
});

await provider.connect();

const txHash = await provider.submitTx(signedTx);
console.log("Transaction submitted:", txHash);

Complete Example

import { HydraProvider, MeshTxBuilder } from "@meshsdk/hydra";
import { MeshCardanoHeadlessWallet, AddressType } from "@meshsdk/wallet";

async function hydraPayment() {
  // Initialize provider
  const provider = new HydraProvider({
    httpUrl: "http://123.45.67.890:4001",
  });

  // Connect to Hydra Head
  await provider.connect();

  // Listen for messages
  provider.onMessage((message) => {
    if (message.tag === "TxValid") {
      console.log("Transaction confirmed in Hydra Head!");
    }
  });

  // Initialize wallet
  const wallet = await MeshCardanoHeadlessWallet.fromCliKeys({
    networkId: 0,
    walletAddressType: AddressType.Enterprise,
    fetcher: provider,
    submitter: provider,
    paymentSkey: "58201aae63d93899640e91b51c5e8bd542262df3ecf3246c3854f39c40f4eb83557d",
  });

  // Get protocol parameters and UTxOs
  const params = await provider.fetchProtocolParameters();
  const utxos = await wallet.getUtxosMesh();
  const changeAddress = await wallet.getChangeAddressBech32();

  // Build transaction
  const txBuilder = new MeshTxBuilder({
    fetcher: provider,
    params: params,
  });

  const unsignedTx = await txBuilder
    .txOut("addr_test1vpd5axpq4qsh8sxvzny49cp22gc5tqx0djf6wmjv5cx7q5qyrzuw8", [
      { unit: "lovelace", quantity: "3000000" },
    ])
    .changeAddress(changeAddress)
    .selectUtxosFrom(utxos)
    .complete();

  // Sign and submit
  const signedTx = await wallet.signTx(unsignedTx, false);
  const txHash = await provider.submitTx(signedTx);

  console.log("Transaction submitted:", txHash);

  // When done, close the head
  // await provider.close();
  // ... wait for contestation period ...
  // await provider.fanout();

  // Disconnect
  await provider.disconnect();
}

Troubleshooting

Connection Failed

Problem: Cannot connect to Hydra node.

Solution:

  1. Verify the Hydra node is running
  2. Check the httpUrl is correct (including port)
  3. Ensure network connectivity to the Hydra node

Head Not Open

Problem: Commands fail with "Head not open" error.

Solution:

  1. Initialize the Head with await provider.init()
  2. Wait for all participants to commit
  3. Check Head state via onMessage events

Transaction Rejected

Problem: Transactions are rejected by the Hydra Head.

Solution:

  1. Verify the transaction is well-formed
  2. Check that inputs exist in the Hydra Head snapshot
  3. Ensure the transaction uses correct protocol parameters from the Head

Contestation Period

Problem: Cannot fanout after closing.

Solution:

  1. Wait for the contestation period to end
  2. The contestation period is configurable when creating the Head
  3. Monitor onMessage for contestation-related events

State Synchronization

Problem: UTxO queries return stale data.

Solution:

  1. Listen to onMessage for SnapshotConfirmed events
  2. The snapshotUtxo in Greetings message contains current state
  3. Ensure you're querying after the latest snapshot is confirmed

On this page