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/hydraInitialize 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
| Parameter | Type | Required | Description |
|---|---|---|---|
httpUrl | string | Yes | The 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>| Parameter | Type | Required | Description |
|---|---|---|---|
timeout | number | No | Timeout 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| Parameter | Type | Required | Description |
|---|---|---|---|
callback | (message: HydraMessage) => void | Yes | Function 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>| Parameter | Type | Required | Description |
|---|---|---|---|
tx.cborHex | string | Yes | The transaction CBOR in hex format |
tx.type | string | Yes | Transaction type (e.g., "Tx ConwayEra") |
tx.description | string | No | Optional 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>| Parameter | Type | Required | Description |
|---|---|---|---|
tx.cborHex | string | Yes | The decommit transaction CBOR in hex format |
tx.type | string | Yes | Transaction type (e.g., "Tx ConwayEra") |
tx.description | string | No | Optional 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[]>| Parameter | Type | Required | Description |
|---|---|---|---|
address | string | Yes | The wallet address |
asset | string | No | Filter 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[]>| Parameter | Type | Required | Description |
|---|---|---|---|
hash | string | Yes | The transaction hash |
index | number | No | Specific 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[]>| Parameter | Type | Required | Description |
|---|---|---|---|
asset | string | Yes | The 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[]>| Parameter | Type | Required | Description |
|---|---|---|---|
policyId | string | Yes | The 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>| Parameter | Type | Required | Description |
|---|---|---|---|
epoch | number | No | Not 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>| Parameter | Type | Required | Description |
|---|---|---|---|
tx | string | Yes | The 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:
- Verify the Hydra node is running
- Check the
httpUrlis correct (including port) - Ensure network connectivity to the Hydra node
Head Not Open
Problem: Commands fail with "Head not open" error.
Solution:
- Initialize the Head with
await provider.init() - Wait for all participants to commit
- Check Head state via
onMessageevents
Transaction Rejected
Problem: Transactions are rejected by the Hydra Head.
Solution:
- Verify the transaction is well-formed
- Check that inputs exist in the Hydra Head snapshot
- Ensure the transaction uses correct protocol parameters from the Head
Contestation Period
Problem: Cannot fanout after closing.
Solution:
- Wait for the contestation period to end
- The contestation period is configurable when creating the Head
- Monitor
onMessagefor contestation-related events
State Synchronization
Problem: UTxO queries return stale data.
Solution:
- Listen to
onMessageforSnapshotConfirmedevents - The
snapshotUtxoinGreetingsmessage contains current state - Ensure you're querying after the latest snapshot is confirmed