API Reference

@hydra-sdk/bridge

Hydra Layer 2 integration and Head lifecycle management

Classes


HydraBridge

Main class for connecting to and managing Hydra Heads.

HydraBridge.constructor(options: InitHydraBridgeOptions) → HydraBridge

Initialize a new Hydra Bridge instance.

Parameters:

  • options (InitHydraBridgeOptions):
    • url?: WebSocket URL (e.g., 'ws://localhost:4001', 'wss://gateway/path?X-Api-Key=...')
    • connector?: Custom connector instance
    • verbose?: Enable verbose logging (default: false)
    • history?: Enable receiving full node history (default: false)
    • noSnapshotUtxo?: Disable snapshot UTxO visibility (default: false)
    • address?: Filter transaction outputs by specific address
    • autoReconnect?: Automatically reconnect when WebSocket drops (default: false)
    • reconnectInterval?: Milliseconds between reconnect attempts (default: 3000)
    • maxReconnectAttempts?: Max reconnect attempts; 0 = unlimited (default: 0)

Example:

import { HydraBridge } from '@hydra-sdk/bridge'

// Direct WebSocket connection
const bridge = new HydraBridge({
    url: 'ws://localhost:4001',
    verbose: true
})

// With advanced options
const bridge = new HydraBridge({
    url: 'ws://localhost:4001',
    verbose: true,
    history: true,
    noSnapshotUtxo: false,
    address: 'addr_test1...'
})

// Hydra Hub connection with API key
const bridge = new HydraBridge({
    url: 'wss://hydra-hub.example.com?X-Api-Key=your_api_key_here',
    verbose: true
})

// Using custom connector
const bridge = new HydraBridge({
    connector: customConnector,
    verbose: true
})

// With auto-reconnect
const bridge = new HydraBridge({
    url: 'ws://localhost:4001',
    autoReconnect: true,
    reconnectInterval: 3000,      // retry every 3s
    maxReconnectAttempts: 10      // give up after 10 attempts (0 = unlimited)
})

Returns: HydraBridge
New HydraBridge instance


Properties

HydraBridge.slotZeroTimestamp: number | null

Unix timestamp (ms) of slot 0, derived from the currentSlot field of the Greetings message (hydra-node v1.3.0+). Use this for slot ↔ time conversion inside the Hydra Head. null until the first Greetings is received.

bridge.events.on('onMessage', payload => {
    if (payload.tag === 'Greetings' && bridge.slotZeroTimestamp !== null) {
        console.log('Slot-zero epoch:', bridge.slotZeroTimestamp)
    }
})

HydraBridge.lastSnapshotNumber: number

The highest snapshot number applied to the in-memory cache. -1 until the first snapshot arrives. Resets to -1 on every reconnect so the cache is properly re-seeded.

console.log('Current snapshot:', bridge.lastSnapshotNumber)

Connection Management

HydraBridge.connect() → Promise<boolean>

Connect to the Hydra Node.

Example:

await bridge.connect()
console.log('Connected to Hydra Node')

Returns: Promise<boolean>


HydraBridge.disconnect() → Promise<boolean>

Disconnect from the Hydra Node.

Example:

await bridge.disconnect()
console.log('Disconnected from Hydra Node')

Returns: Promise<boolean>


HydraBridge.connected() → boolean

Check the connection status.

Example:

const isConnected = bridge.connected()
console.log('Connection status:', isConnected)

Returns: boolean
True if connected, false otherwise


Head Information

HydraBridge.headInfo() → Promise<HeadInfo>

Get information about the current Head.

Example:

const info = await bridge.headInfo()
console.log({
    headId: info.headId,
    headStatus: info.headStatus,
    vkey: info.vkey
})

Returns: Promise<HeadInfo>
Head information object


Protocol Parameters

HydraBridge.getProtocolParameters() → Promise<Protocol>

Get Cardano protocol parameters.

Example:

const protocolParams = await bridge.getProtocolParameters()
console.log({
    minFeeA: protocolParams.min_fee_a,
    minFeeB: protocolParams.min_fee_b,
    maxTxSize: protocolParams.max_tx_size,
    keyDeposit: protocolParams.key_deposit,
    poolDeposit: protocolParams.pool_deposit
})

Returns: Promise<Protocol>
Protocol parameters object


UTxO Management

HydraBridge.querySnapshotUtxo() → Promise<UTxOObject>

Query UTxOs in the current snapshot.

Example:

const utxoObject = await bridge.querySnapshotUtxo()
console.log('Snapshot UTxO:', utxoObject)

Returns: Promise<UTxOObject>
UTxO object from snapshot


HydraBridge.snapshotUtxoArray() → UTxO[]

Get UTxOs array from snapshot.

Example:

const utxoArray = bridge.snapshotUtxoArray()
console.log('UTxO array:', utxoArray)

Returns: UTxO[]
Array of UTxOs


HydraBridge.getAddressBalance(address: string) → Map<string, bigint> | null

O(1) balance lookup from the pre-computed in-memory cache. Rebuilt automatically on every snapshot event — no I/O per call.

  • Returns null on cold start (cache not yet seeded) — caller should fall back to DB or call querySnapshotUtxo() first.
  • Returns an empty Map when the address is in the Head but holds no UTxOs.
  • Keys are asset units ('lovelace', 'policyId+assetName'); values are bigint.

Parameters:

  • address (string): Cardano address (bech32)

Example:

const balance = bridge.getAddressBalance(account.baseAddressBech32)

if (balance === null) {
    console.log('Cache not seeded yet — wait for Greetings or call querySnapshotUtxo()')
} else {
    const lovelace = balance.get('lovelace') ?? 0n
    console.log('ADA balance:', Number(lovelace) / 1_000_000)

    for (const [unit, qty] of balance) {
        if (unit !== 'lovelace') console.log(unit, qty.toString())
    }
}

Returns: Map<string, bigint> | null


HydraBridge.queryAddressUTxO(address: string) → Promise<UTxO[]>

Query UTxOs for a specific address.

Parameters:

  • address (string): Cardano address (bech32)

Example:

const account = wallet.getAccount(0, 0)
const addressUtxos = await bridge.queryAddressUTxO(account.baseAddressBech32)
console.log('Address UTxOs:', addressUtxos)

Returns: Promise<UTxO[]>
Array of UTxOs for the address


HydraBridge.addressesInHead() → Promise<string[]>

Get all addresses in the current Head.

Example:

const addresses = await bridge.addressesInHead()
console.log('Addresses in Head:', addresses)

Returns: Promise<string[]>
Array of addresses


Transaction Operations

HydraBridge.submitTxSync(transaction: Transaction, options?: SubmitOptions) → Promise<TxResult>

Submit transaction and wait for confirmation.

Parameters:

  • transaction (Transaction): Transaction to submit
  • options? (SubmitOptions): Submission options
    • timeout?: Timeout in milliseconds

Example:

const result = await bridge.submitTxSync(
    {
        type: 'Witnessed Tx ConwayEra',
        description: 'Ledger Cddl Format',
        cborHex: signedTxCbor,
        txId: transactionId
    },
    { timeout: 30000 }
)

console.log({
    txId: result.txId,
    isValid: result.isValid,
    isConfirmed: result.isConfirmed
})

Returns: Promise<TxResult>
Transaction result object


HydraBridge.submitTx(transaction: Transaction, callback: SubmitTxCallback, options?: SubmitOptions) → void

Submit a transaction using a Node.js error-first callback. Non-blocking — returns immediately.

Parameters:

  • transaction (Transaction): Transaction to submit
  • callback (error: SubmitTxError | null, result: SubmitTxResult | null) => void: Called once when the transaction is confirmed, rejected, or times out
  • options? (SubmitOptions): { timeout?: number } — default 30 000ms

Example:

bridge.submitTx(
    { type: 'Witnessed Tx ConwayEra', description: '', cborHex: signedTxCbor, txId },
    (error, result) => {
        if (error) {
            console.error('Submit failed:', error.reason)
            return
        }
        console.log('Confirmed in snapshot:', result!.result?.snapshot?.number)
    },
    { timeout: 30000 }
)

Returns: void


HydraBridge.commit(data: CommitData) → Promise<any>

Commit UTxOs into the Head.

Parameters:

  • data (CommitData): Commit data object

Example:

const result = await bridge.commit({
    cborHex: commitTxCbor,
    description: 'Commit transaction'
})
console.log('Commit result:', result)

Returns: Promise<any>


HydraBridge.submitCardanoTransaction(data: TransactionData) → Promise<any>

Submit transaction to Cardano network.

Parameters:

  • data (TransactionData): Transaction data object

Example:

const result = await bridge.submitCardanoTransaction({
    cborHex: txCbor,
    description: 'Cardano transaction'
})
console.log('Cardano transaction submitted:', result)

Returns: Promise<any>


Commands

HydraBridge.commands.init() → void

Initialize a new Hydra Head.

Example:

bridge.commands.init()

HydraBridge.commands.close() → void

Close the Hydra Head.

Example:

bridge.commands.close()

HydraBridge.commands.safeClose() → void

Close the Head only when it holds no non-ADA assets. The node replies with InvalidInput when assets are present.

Example:

bridge.commands.safeClose()

HydraBridge.commands.sideLoadSnapshot(snapshot) → void

Side-load a confirmed snapshot into the Head.

Example:

bridge.commands.sideLoadSnapshot(confirmedSnapshot)

HydraBridge.commands.partialFanout(utxo) → void

Fan out a client-selected subset of a closed Head's UTxO.

Requires a hydra-node newer than 2.3.0. Older nodes reply with InvalidInput.

Example:

bridge.commands.partialFanout(utxoToFanout)

HydraBridge.submitL2Tx(tx, options?) → Promise<SubmitL2TxResponse>

Submit an L2 transaction over POST /transaction and let the node return the verdict, instead of racing WebSocket messages against a client-side timeout. Prefer this over submitTxSync().

Example:

const res = await bridge.submitL2Tx(tx)
switch (res.tag) {
    case 'SubmitTxConfirmed': console.log('snapshot', res.snapshotNumber); break
    case 'SubmitTxInvalid':   console.error(res.validationError); break
    case 'SubmitTxRejected':  console.error(res.reason); break // node out of sync
    case 'SubmitTxSubmitted': break // accepted, not yet confirmed
}

HydraBridge.pendingDeposits() → Promise<string[]>

Transaction ids of deposits observed on chain but not yet included in a snapshot.

Example:

const pending = await bridge.pendingDeposits()

HydraBridge.recoverDeposit(depositTxId, options?) → Promise<string | void>

Recover a pending deposit back to L1. Deposits stay recoverable after a Head closes, so this is also how you reclaim funds from a previous Head.

Example:

await bridge.recoverDeposit(depositTxId)

HydraBridge.getRawProtocolParameters() → Promise<RawProtocolParameters>

The node's protocol parameters unmodified — including costModels and protocolVersion, which getProtocolParameters() drops. Use this when budgeting Plutus ExUnits.

Example:

const raw = await bridge.getRawProtocolParameters()
raw.costModels.PlutusV3 // 350 entries under PV11

HydraBridge.commands.fanout() → void

Perform fanout after closing Head.

Example:

bridge.commands.fanout()

HydraBridge.commands.contest() → void

Contest the Head closure.

Example:

bridge.commands.contest()

HydraBridge.commands.recover(recoverTxId: string) → void

Recover from a specific transaction.

Parameters:

  • recoverTxId (string): Transaction hash to recover from

Example:

bridge.commands.recover('tx_hash_here')

HydraBridge.commands.newTx(cborHex: string, description: string, callback?: Function) → void

Send a new transaction to the Head.

Parameters:

  • cborHex (string): Transaction in CBOR hex format
  • description (string): Transaction description
  • callback? (Function): Callback function when sent

Example:

bridge.commands.newTx(signedTxCbor, 'Payment transaction', () =>
    console.log('Transaction sent to Head')
)

HydraBridge.commands.decommit(payload: DecommitPayload) → Promise<DecommitResult>

Decommit UTxOs from the Head.

Parameters:

  • payload (DecommitPayload): Decommit data
    • cborHex: Transaction CBOR hex
    • txId: Transaction ID
    • timeout?: Timeout value

Example:

const result = await bridge.commands.decommit({
    cborHex: decommitTxCbor,
    txId: transactionId,
    timeout: 30000
})
console.log('Decommit result:', result)

Returns: Promise<DecommitResult>


HydraBridge.commands.initSync(retry?: number, interval?: number) → Promise<any>

Initialize Head with retry logic.

Parameters:

  • retry? (number): Number of retries. Default: 3
  • interval? (number): Interval between retries (ms). Default: 20000

Example:

const result = await bridge.commands.initSync(3, 20000)
console.log('Head initialized:', result)

Returns: Promise<any>


Connectors

WebsocketConnector

Basic WebSocket connector for direct connection to Hydra Node.

Constructor Parameters:

  • websocketUrl (string): WebSocket URL of the Hydra Node
  • history? (boolean): Enable receiving full node history (default: false)
  • noSnapshotUtxo? (boolean): Disable snapshot UTxO visibility (default: false)
  • address? (string): Filter transaction outputs by specific address

Constructor:

import { WebsocketConnector } from '@hydra-sdk/bridge'

// Basic connection
const connector = new WebsocketConnector({
    websocketUrl: 'ws://localhost:4001'
})

// With advanced options
const connector = new WebsocketConnector({
    websocketUrl: 'ws://localhost:4001',
    history: true,
    noSnapshotUtxo: false,
    address: 'addr_test1...'
})

// Hydra Hub connection with API key in URL
const connector = new WebsocketConnector({
    websocketUrl: 'wss://hydra-hub.example.com?X-Api-Key=your_api_key_here'
})

// Combine API key with other options
const connector = new WebsocketConnector({
    websocketUrl: 'wss://hydra-hub.example.com?X-Api-Key=your_api_key_here',
    history: true,
    address: 'addr_test1...'
})

const bridge = new HydraBridge({
    connector: connector,
    verbose: true
})

HexcoreConnector

Advanced connector for Hexcore API integration with Socket.IO authentication.

Constructor:

import { HexcoreConnector } from '@hydra-sdk/bridge'

// Constructor is positional: new HexcoreConnector(socketIoUrl, options?)
const connector = new HexcoreConnector('wss://api.hexcore.io/hydra', {
    socketIoOptions: {
        auth: {
            token: 'your_jwt_token'
        },
        transports: ['websocket'],
        timeout: 20000
    },
    namespace: 'hydra'
})

const bridge = new HydraBridge({
    connector: connector,
    verbose: true
})

await bridge.connect()

Event Handling

Bridge provides comprehensive event handling through the events property:

Connection Events

// Connection established
bridge.events.on('onConnected', () => {
    console.log('Connected to Hydra Node')
})

// Connection lost
bridge.events.on('onDisconnected', () => {
    console.log('Disconnected from Hydra Node')
})

// Connection error
bridge.events.on('onConnectError', error => {
    console.error('Connection error:', error)
})

Head Lifecycle Events

bridge.events.on('onMessage', payload => {
    console.log('Message received:', payload.tag, payload.timestamp)

    switch (payload.tag) {
        case 'Greetings':
            console.log('Greeting from Head:', {
                headStatus: payload.headStatus,
                headId: payload.hydraHeadId,
                // v1.3.0 fields
                currentSlot: payload.currentSlot,
                chainSyncedStatus: payload.chainSyncedStatus,
                nodeVersion: payload.hydraNodeVersion
            })
            // slotZeroTimestamp is automatically derived when currentSlot is present
            console.log('Slot-zero timestamp:', bridge.slotZeroTimestamp)
            break

        case 'HeadIsOpen':
            console.log('Head is open and ready for transactions!')
            break

        case 'HeadIsClosed':
            console.log('Head closed, performing fanout...')
            break

        case 'HeadIsFinalized':
            console.log('Head lifecycle completed')
            break

        case 'TxValid':
            console.log('Transaction is valid:', payload.transaction)
            break

        case 'TxInvalid':
            console.log('Transaction is invalid:', {
                transaction: payload.transaction,
                error: payload.validationError
            })
            break

        case 'SnapshotConfirmed':
            console.log('Snapshot confirmed')
            break

        case 'CommitFinalized':
            console.log('Deposit finalized:', payload.depositTxId)
            break

        case 'DecommitApproved':
            console.log('Decommit approved:', payload.decommitTxId)
            break

        case 'DecommitFinalized':
            console.log('Decommit finalized')
            break

        case 'CommandFailed':
            console.error('Command failed:', payload.clientInput)
            break
    }
})

Types and Interfaces

InitHydraBridgeOptions

type InitHydraBridgeOptions = {
    verbose?: boolean
    /** Automatically reconnect on WebSocket drop. Default: false */
    autoReconnect?: boolean
    /** Milliseconds between reconnect attempts. Default: 3000 */
    reconnectInterval?: number
    /** Max reconnect attempts; 0 = unlimited. Default: 0 */
    maxReconnectAttempts?: number
} & (
    | { url: string; history?: boolean; noSnapshotUtxo?: boolean; address?: string }
    | { connector: HydraConnector }
)

SubmitTxResult / SubmitTxError

type SubmitTxResult = {
    txId: string
    isValid: boolean
    isConfirmed: boolean
    result: Readonly<SnapshotConfirmed> | null
}

type SubmitTxError = {
    txId: string
    reason: string
    tag: string
}

HydraPayload

interface HydraPayload {
    tag: HydraHeadTag
    timestamp: string
    seq?: number
    [key: string]: any
}

HydraHeadStatus

enum HydraHeadStatus {
    Idle = 'Idle',
    Open = 'Open',
    Closed = 'Closed',
    FanoutPossible = 'FanoutPossible',
    /** Requires a hydra-node newer than 2.3.0 (selective partial fanout). */
    FanningOut = 'FanningOut'
}

Protocol

interface Protocol {
    min_fee_a: number
    min_fee_b: number
    max_tx_size: number
    key_deposit: string
    pool_deposit: string
    [key: string]: any
}

Complete Examples

Basic Head Management

import { HydraBridge } from '@hydra-sdk/bridge'

async function manageHydraHead() {
    const bridge = new HydraBridge({
        url: 'ws://localhost:4001',
        verbose: true
    })

    // Set up event handlers before connecting
    bridge.events.on('onConnected', () => {
        console.log('Connected! Initializing Head...')
        bridge.commands.init()
    })

    bridge.events.on('onMessage', payload => {
        switch (payload.tag) {
            case 'HeadIsOpen':
                console.log('Head is open and ready for transactions!')
                break
            case 'TxValid':
                console.log('Transaction confirmed:', payload.transaction.txId)
                break
            case 'HeadIsClosed':
                console.log('Head closed, performing fanout...')
                bridge.commands.fanout()
                break
            case 'HeadIsFinalized':
                console.log('Head lifecycle completed')
                bridge.disconnect()
                break
        }
    })

    // Connect to start the process
    await bridge.connect()
}

Processing Transactions in Hydra Head

import { HydraBridge } from '@hydra-sdk/bridge'
import { TxBuilder } from '@hydra-sdk/transaction'
import { AppWallet } from '@hydra-sdk/core'

async function processHydraTransactions() {
    const bridge = new HydraBridge({
        url: 'ws://localhost:4001',
        verbose: true
    })

    const wallet = new AppWallet({
        networkId: 0,
        key: { type: 'mnemonic', words: AppWallet.brew() }
    })

    bridge.events.on('onConnected', async () => {
        // Get protocol parameters
        const protocolParams = await bridge.getProtocolParameters()

        // Create transaction builder
        const txBuilder = new TxBuilder({
            isHydra: true,
            params: protocolParams
        })

        // Get account and UTxOs
        const account = wallet.getAccount(0, 0)
        const addressUtxos = await bridge.queryAddressUTxO(account.baseAddressBech32)

        if (addressUtxos.length === 0) {
            console.log('No UTxOs found for address')
            return
        }

        // Build and sign transaction
        const tx = await txBuilder
            .setInputs(addressUtxos)
            .txOut(account.baseAddressBech32, [
                { unit: 'lovelace', quantity: '1000000' }
            ])
            .changeAddress(account.baseAddressBech32)
            .complete()

        const signedTx = await wallet.signTx(tx.to_hex(), false, 0, 0)

        // Submit to Hydra Head
        const result = await bridge.submitTxSync(
            {
                type: 'Witnessed Tx ConwayEra',
                description: 'Ledger Cddl Format',
                cborHex: signedTx,
                txId: tx.id
            },
            { timeout: 30000 }
        )

        console.log('Transaction successful:', result)
    })

    await bridge.connect()
}

Advanced Hexcore Integration

import { HydraBridge, HexcoreConnector } from '@hydra-sdk/bridge'

async function connectToHexcore() {
    const connector = new HexcoreConnector('wss://api.hexcore.io/hydra', {
        socketIoOptions: {
            auth: {
                token: 'your_jwt_token'
            },
            transports: ['websocket'],
            timeout: 20000
        },
        namespace: 'hydra'
    })

    const bridge = new HydraBridge({
        connector: connector,
        verbose: true
    })

    bridge.events.on('onConnected', () => {
        console.log('Authenticated and connected to Hexcore')
    })

    bridge.events.on('onConnectError', error => {
        console.error('Hexcore connection error:', error)
    })

    await bridge.connect()
}

Advanced Hydra Hub Integration

import { HydraBridge } from '@hydra-sdk/bridge'

async function connectWithApiKey() {
    // Pass API key directly in URL
    const bridge = new HydraBridge({
        url: 'wss://hydra-hub.example.com?X-Api-Key=your_secret_api_key',
        verbose: true
    })

    bridge.events.on('onConnected', () => {
        console.log('Authenticated and connected to Hydra Hub')
    })

    bridge.events.on('onConnectError', error => {
        console.error('Connection error:', error)
    })

    await bridge.connect()
}

Best Practices

  1. Event Setup: Always set up event handlers before calling connect()
  2. Balance reads: Prefer getAddressBalance() (O(1), no I/O) over queryAddressUTxO() when you only need amounts — the cache is kept fresh automatically on every SnapshotConfirmed
  3. Auto-reconnect: Use autoReconnect: true in production; disconnect() cancels the reconnect loop cleanly
  4. Callback vs Promise: Use submitTx (callback) when you need non-blocking fire-and-forget; use submitTxSync (Promise) when you need to await confirmation before continuing
  5. Authentication:
    • For Hydra Hub: Pass API key in URL query params (?X-Api-Key=your_key)
    • For Hexcore: Use JWT tokens in Socket.IO auth options
  6. Resource Cleanup: Always call disconnect() when done
  7. Slot arithmetic: Use bridge.slotZeroTimestamp + TimeUtils from @hydra-sdk/core for slot ↔ time conversion inside the Head
  8. Timeout Configuration: Set appropriate timeouts for submitTxSync / submitTx (default 30 000ms)
  9. Logging: Enable verbose: true for debugging during development