Skip to the content.

Usage Guide

This guide covers how to use Hydra-Yaci for developing Layer 2 payment applications on Cardano.

Overview

Hydra-Yaci provides a complete workflow for:

  1. Generating cryptographic keys
  2. Funding participant wallets
  3. Starting Hydra nodes
  4. Opening Hydra heads
  5. Committing funds to heads
  6. Sending instant payments
  7. Closing Hydra heads
  8. Monitoring the system

NPM Scripts Reference

Quick reference of all available commands:

npm run prerequisites      # Check system requirements
npm run setup             # Setup project directories and binaries
npm run keys:generate     # Generate participant keys
npm run wallets:fund      # Fund participant wallets
npm run hydra:start       # Start all Hydra nodes
npm run hydra:stop        # Stop all Hydra nodes
npm run reset             # Reset all state and keys
npm run example:status    # Check Yaci DevKit status
npm run example:addresses # Generate and display addresses
npm run example:fund      # Fund from faucet
npm run example:open      # Open a Hydra head
npm run example:workflow  # Run complete workflow

Workflow: Creating Your First Hydra Head

Step 1: Generate Keys

Generate cryptographic keys for all participants:

npm run keys:generate

This creates keys in hydra-nodes/{participant}/keys/:

hydra-nodes/
├── alice/keys/
│   ├── cardano.sk       # Cardano signing key
│   ├── cardano.vk       # Cardano verification key
│   ├── hydra.sk         # Hydra signing key
│   └── hydra.vk         # Hydra verification key
├── bob/keys/
└── carol/keys/

Output Example:

Generating keys for alice...
  ✓ Hydra keys generated
  ✓ Cardano keys generated
  ✓ Address: addr_test1vq2kn3z4agl8...

Generating keys for bob...
  ✓ Hydra keys generated
  ✓ Cardano keys generated
  ✓ Address: addr_test1vr8hx7m9pcw5...

Generating keys for carol...
  ✓ Hydra keys generated
  ✓ Cardano keys generated
  ✓ Address: addr_test1vs9mp2x5vjh8...

Step 2: Fund Wallets

Fund participant wallets using Yaci DevKit:

npm run wallets:fund

Manual Funding (alternative):

Using Yaci CLI:

# In Yaci CLI
topup addr_test1vq2kn3z4agl8... 1000
topup addr_test1vr8hx7m9pcw5... 1000
topup addr_test1vs9mp2x5vjh8... 1000

Verify Funding:

node examples/check-yaci.js

Step 3: Publish Hydra Scripts

Publish Hydra protocol scripts to the blockchain:

./bin/hydra-node publish-scripts \
  --testnet-magic 42 \
  --node-socket /clusters/nodes/default/node/node.sock \
  --cardano-signing-key hydra-nodes/alice/keys/cardano.sk

Copy the output transaction IDs and add them to .env:

# Example output:
# νInitial: 0319848a47b887b565a08ddff6b61e2e19fff35072d6e0b57028bfb63577ddba
# νCommit: d46e139e4ffd649d5972e8db11ac68a4a373eca74747b2643f6d305dd0661d9f
# νHead: d044154a3dd15ea53edc23f84a0306611249f8f74025ae7cc60aedb0a99e1971

# Add to .env:
HYDRA_SCRIPTS_TX_ID="0319848a...,d46e139e...,d044154a..."

Step 4: Start Hydra Nodes

Start all three Hydra nodes:

npm run hydra:start

This starts Docker containers for Alice, Bob, and Carol.

Verify nodes are running:

docker ps

Expected output:

CONTAINER ID   IMAGE                                    STATUS
abc123...      ghcr.io/cardano-scaling/hydra-node:1.2.0 Up 10 seconds
def456...      ghcr.io/cardano-scaling/hydra-node:1.2.0 Up 10 seconds
ghi789...      ghcr.io/cardano-scaling/hydra-node:1.2.0 Up 10 seconds

Check logs:

# Alice's logs
docker logs hydra-alice

# Bob's logs
docker logs hydra-bob

# Carol's logs
docker logs hydra-carol

Step 5: Open a Hydra Head

Use the WebSocket API to open a head:

node examples/open-hydra-head.js

Or manually via WebSocket client:

Connect to WebSocket:

ws://localhost:4001

Send Init message:

{
  "tag": "Init"
}

Expected Response:

{
  "tag": "HeadIsInitializing",
  "headId": "4ea01a230b166e43748e28b9ecc729dcfca925038be66d7f9a3238b8",
  "parties": [...]
}

Step 6: Commit Funds

Each participant must commit funds to the head:

node examples/commit-fund.js

Manual Commit via WebSocket:

{
  "tag": "Commit",
  "utxo": {
    "txId": "transaction-id",
    "index": 0
  }
}

Wait for all participants to commit:

{
  "tag": "HeadIsOpen",
  "utxo": {...}
}

Step 7: Send Payments

Once the head is open, send instant payments:

node examples/send-payment.js

Manual Payment via WebSocket:

{
  "tag": "NewTx",
  "transaction": {
    "type": "Tx ConwayEra",
    "description": "",
    "cborHex": "84a500818258..."
  }
}

Payment Confirmed:

{
  "tag": "TxValid",
  "transaction": {...},
  "utxo": {...}
}

Step 8: Close the Head

Close the Hydra head when finished:

node examples/close-head.js

Manual Close via WebSocket:

{
  "tag": "Close"
}

Head Finalized:

{
  "tag": "HeadIsFinalized",
  "utxo": {...}
}

Working with the Hydra TUI

Starting the TUI

The Hydra TUI (Terminal User Interface) provides a visual interface for managing Hydra heads.

Launch for Alice:

docker run --rm -it \
  --name hydra-tui-alice \
  -v "$PWD/hydra-nodes:/app/hydra-nodes" \
  --platform linux/amd64 \
  ghcr.io/cardano-scaling/hydra-tui:1.2.0 \
  --connect host.docker.internal:4001 \
  --cardano-signing-key /app/hydra-nodes/alice/keys/cardano.sk \
  --testnet-magic 42

Launch for Bob:

docker run --rm -it \
  --name hydra-tui-bob \
  -v "$PWD/hydra-nodes:/app/hydra-nodes" \
  --platform linux/amd64 \
  ghcr.io/cardano-scaling/hydra-tui:1.2.0 \
  --connect host.docker.internal:4002 \
  --cardano-signing-key /app/hydra-nodes/bob/keys/cardano.sk \
  --testnet-magic 42

Launch for Carol:

docker run --rm -it \
  --name hydra-tui-carol \
  -v "$PWD/hydra-nodes:/app/hydra-nodes" \
  --platform linux/amd64 \
  ghcr.io/cardano-scaling/hydra-tui:1.2.0 \
  --connect host.docker.internal:4003 \
  --cardano-signing-key /app/hydra-nodes/carol/keys/cardano.sk \
  --testnet-magic 42

TUI Features

The TUI shows:

TUI Controls

Key Action
i Initialize head
c Commit funds
n Create new transaction
x Close head
q Quit
↑↓ Scroll logs

WebSocket API Usage

Connecting to the API

Each Hydra node exposes a WebSocket API:

Client Examples

Using wscat

# Install wscat
npm install -g wscat

# Connect to Alice
wscat -c ws://localhost:4001

# Send a message
> {"tag": "Init"}

Using JavaScript

import WebSocket from 'ws';

const ws = new WebSocket('ws://localhost:4001');

ws.on('open', () => {
  console.log('Connected to Hydra node');
  
  // Initialize head
  ws.send(JSON.stringify({ tag: 'Init' }));
});

ws.on('message', (data) => {
  const message = JSON.parse(data);
  console.log('Received:', message);
});

Using Python

import websocket
import json

def on_message(ws, message):
    data = json.loads(message)
    print('Received:', data)

def on_open(ws):
    print('Connected')
    ws.send(json.dumps({'tag': 'Init'}))

ws = websocket.WebSocketApp(
    'ws://localhost:4001',
    on_message=on_message,
    on_open=on_open
)

ws.run_forever()

Message Types

Client → Server Messages

Initialize Head:

{"tag": "Init"}

Commit Funds:

{
  "tag": "Commit",
  "utxo": {
    "txId": "...",
    "index": 0
  }
}

Send Transaction:

{
  "tag": "NewTx",
  "transaction": {
    "type": "Tx ConwayEra",
    "cborHex": "..."
  }
}

Close Head:

{"tag": "Close"}

Abort Head (during initialization):

{"tag": "Abort"}

Server → Client Messages

Greetings (connection established):

{
  "tag": "Greetings",
  "me": {"vkey": "..."},
  "headStatus": "Idle",
  "hydraNodeVersion": "1.2.0",
  "networkInfo": {
    "networkConnected": true,
    "peersInfo": {...}
  }
}

Head Initializing:

{
  "tag": "HeadIsInitializing",
  "headId": "...",
  "parties": [...]
}

Committed (funds committed):

{
  "tag": "Committed",
  "party": {"vkey": "..."},
  "utxo": {...}
}

Head Opened:

{
  "tag": "HeadIsOpen",
  "utxo": {...}
}

Transaction Valid:

{
  "tag": "TxValid",
  "transaction": {...},
  "utxo": {...}
}

Snapshot Confirmed:

{
  "tag": "SnapshotConfirmed",
  "snapshot": {
    "snapshotNumber": 1,
    "utxo": {...}
  }
}

Head Closed:

{
  "tag": "HeadIsClosed",
  "snapshotNumber": 5
}

Head Finalized:

{
  "tag": "HeadIsFinalized",
  "utxo": {...}
}

Monitoring and Debugging

Check Node Status

# Check if nodes are running
docker ps | grep hydra

# View logs
docker logs hydra-alice --tail 50 --follow
docker logs hydra-bob --tail 50 --follow
docker logs hydra-carol --tail 50 --follow

Prometheus Metrics

Access metrics at:

Grafana Dashboards

Start monitoring stack:

cd monitoring
docker compose -f docker-compose.monitoring.yml up -d

Access Grafana:

WebSocket Debugging

Use Postman or any WebSocket client:

  1. Create a new WebSocket request
  2. URL: ws://localhost:4001
  3. Connect and view messages
  4. Send custom messages

Advanced Usage

Multi-Head Management

Run multiple Hydra heads simultaneously:

// Head 1: Alice, Bob
const head1 = new WebSocket('ws://localhost:4001');

// Head 2: Bob, Carol
const head2 = new WebSocket('ws://localhost:4002');

// Manage separately
head1.on('message', handleHead1Message);
head2.on('message', handleHead2Message);

Custom Transaction Building

Use Lucid Evolution to build custom transactions:

import { Lucid } from '@lucid-evolution/lucid';

const lucid = await Lucid.new();

const tx = await lucid
  .newTx()
  .payToAddress('addr_test1...', { lovelace: 1000000n })
  .complete();

// Submit to Hydra
ws.send(JSON.stringify({
  tag: 'NewTx',
  transaction: {
    type: 'Tx ConwayEra',
    cborHex: tx.toString()
  }
}));

Automated Testing

Create test scripts:

// test-hydra-flow.js
async function testHydraFlow() {
  // 1. Initialize
  await initHead();
  
  // 2. Commit
  await commitFunds();
  
  // 3. Send payments
  for (let i = 0; i < 10; i++) {
    await sendPayment(i);
  }
  
  // 4. Close
  await closeHead();
}

testHydraFlow().catch(console.error);

Best Practices

1. Always Check Status

Before operations:

npm run example:status

2. Backup Keys

# Backup to secure location
cp -r hydra-nodes/alice/keys ~/secure-backup/

3. Monitor Logs

Keep logs running:

docker logs hydra-alice --follow &
docker logs hydra-bob --follow &
docker logs hydra-carol --follow &

4. Clean Shutdown

Always stop nodes properly:

npm run hydra:stop

5. Reset When Needed

If state becomes inconsistent:

npm run reset
npm run keys:generate
npm run wallets:fund
npm run hydra:start

Common Tasks

Check Wallet Balances

node -e "
import axios from 'axios';
const addr = 'addr_test1vq2kn...';
axios.get(\`http://localhost:8080/api/v1/addresses/\${addr}\`)
  .then(r => console.log(r.data));
"

List All Running Containers

docker ps --format "table \t\t"

View Network Topology

# Check peer connections
curl http://localhost:4001 | jq '.networkInfo'

Next Steps


Need help? Check the Troubleshooting Guide or open an issue on GitHub.