@hydra-sdk/bridge
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 instanceverbose?: 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 addressautoReconnect?: 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
nullon cold start (cache not yet seeded) — caller should fall back to DB or callquerySnapshotUtxo()first. - Returns an empty
Mapwhen the address is in the Head but holds no UTxOs. - Keys are asset units (
'lovelace','policyId+assetName'); values arebigint.
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 submitoptions?(SubmitOptions): Submission optionstimeout?: 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 submitcallback(error: SubmitTxError | null, result: SubmitTxResult | null) => void: Called once when the transaction is confirmed, rejected, or times outoptions?(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.
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 formatdescription(string): Transaction descriptioncallback?(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 datacborHex: Transaction CBOR hextxId: Transaction IDtimeout?: 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: 3interval?(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 Nodehistory?(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
- Event Setup: Always set up event handlers before calling
connect() - Balance reads: Prefer
getAddressBalance()(O(1), no I/O) overqueryAddressUTxO()when you only need amounts — the cache is kept fresh automatically on everySnapshotConfirmed - Auto-reconnect: Use
autoReconnect: truein production;disconnect()cancels the reconnect loop cleanly - Callback vs Promise: Use
submitTx(callback) when you need non-blocking fire-and-forget; usesubmitTxSync(Promise) when you need toawaitconfirmation before continuing - 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
- For Hydra Hub: Pass API key in URL query params (
- Resource Cleanup: Always call
disconnect()when done - Slot arithmetic: Use
bridge.slotZeroTimestamp+TimeUtilsfrom@hydra-sdk/corefor slot ↔ time conversion inside the Head - Timeout Configuration: Set appropriate timeouts for
submitTxSync/submitTx(default 30 000ms) - Logging: Enable
verbose: truefor debugging during development
Related Documentation
- Core API - Wallet and utilities
- Transaction Builder API - Build transactions
