Guides
Hydra Integration Examples
Examples of integrating with Hydra Network using real patterns
Examples based on real integration patterns used in the Hydra SDK ecosystem.
Basic Hydra Transaction Building
import { TxBuilder } from '@hydra-sdk/transaction'
// Hydra transactions have different fees from Cardano mainnet
const txBuilder = new TxBuilder({
isHydra: true,
params: {
minFeeA: 0, // Hydra uses a different fee structure
minFeeB: 0
}
})
// Build the transaction as usual
const tx = await txBuilder
.setInputs(utxos)
.addOutput({
address: recipientAddress,
amount: [{ unit: 'lovelace', quantity: '1000000' }]
})
.setChangeAddress(changeAddress)
.complete()
console.log('Hydra Transaction:', tx.to_hex())
Hydra Bridge Connection (From Real Usage Patterns)
import { HydraBridge, HexcoreConnector } from '@hydra-sdk/bridge'
// Socket.IO connection with authentication
const bridge = new HydraBridge({
connector: new HexcoreConnector(
'wss://alpha-v1-api.hexcore.io.vn',
{
socketIoOptions: {
auth: { token: 'your-jwt-token-here' }
}
}
)
})
// Event handlers following real-world patterns
bridge.events.on('onConnected', () => {
console.log('✅ Connected to Hydra Head')
})
bridge.events.on('onDisconnected', () => {
console.log('❌ Disconnected from Hydra Head')
})
// Head/Tx state is delivered through onMessage — switch on payload.tag
bridge.events.on('onMessage', (payload) => {
switch (payload.tag) {
case 'HeadIsOpen':
console.log('🚀 Hydra Head is open:', payload)
break
case 'HeadIsClosed':
console.log('🔒 Hydra Head is closed:', payload)
break
case 'TxValid':
console.log('✅ Transaction valid:', payload.transaction)
break
case 'TxInvalid':
console.log('❌ Transaction invalid:', payload.transaction, payload.validationError)
break
}
})
// Connect and clean up
await bridge.connect()
// Cleanup trong Vue/React lifecycle
// onBeforeUnmount(() => bridge?.disconnect())
Custom API Integration với Hydra
// Pattern from nodejs-playground for Hydra API calls
import { Converter, UTxO } from '@hydra-sdk/core'
import axios from 'axios'
class HydraHexcoreApi {
private instance = axios.create({
baseURL: 'https://alpha-v1-api.hexcore.io.vn',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.HEXCORE_API_TOKEN}`
}
})
async queryAddressUTxO(address: string): Promise<UTxO[]> {
try {
const response = await this.instance.get(`hydra-main/utxo/${address}`)
if (response.data.data) {
return Converter.convertUTxOObjectToUTxO(response.data.data)
} else {
throw new Error('No UTxO data found in Hydra response')
}
} catch (error) {
console.error('Error querying Hydra UTxO:', error)
throw error
}
}
async submitHydraTx(cborHex: string) {
try {
const response = await this.instance.post('/hydra-main/tx', {
cborHex
})
return response.data.txHash
} catch (error) {
console.error('Error submitting to Hydra:', error)
throw error
}
}
}
// Usage
const hydraApi = new HydraHexcoreApi()
const utxos = await hydraApi.queryAddressUTxO(walletAddress)
Environment-specific Configuration
// Pattern cho environment configuration
import { NETWORK_ID } from '@hydra-sdk/core'
const getNetworkConfig = (isHydra: boolean = false) => {
if (isHydra) {
return {
networkId: NETWORK_ID.PREPROD, // Hydra uses the preprod network
apiEndpoint: 'https://alpha-v1-api.hexcore.io.vn',
socketEndpoint: 'wss://alpha-v1-api.hexcore.io.vn',
isHydra: true,
txParams: {
minFeeA: 0,
minFeeB: 0
}
}
} else {
return {
networkId: NETWORK_ID.PREPROD,
apiEndpoint: 'https://preprod.blockfrost.io/api/v0',
socketEndpoint: null,
isHydra: false,
txParams: {
minFeeA: 44,
minFeeB: 155381
}
}
}
}
// Usage trong application
const config = getNetworkConfig(process.env.USE_HYDRA === 'true')
const txBuilder = new TxBuilder({
isHydra: config.isHydra,
params: config.txParams
})
Complete Hydra Integration Example
import { AppWallet, NETWORK_ID } from '@hydra-sdk/core'
import { TxBuilder } from '@hydra-sdk/transaction'
import { HydraBridge, HexcoreConnector } from '@hydra-sdk/bridge'
class HydraWalletManager {
private wallet: AppWallet
private bridge: HydraBridge
private hydraApi: HydraHexcoreApi
constructor(mnemonic: string[], jwtToken: string) {
this.wallet = new AppWallet({
key: { type: 'mnemonic', words: mnemonic },
networkId: NETWORK_ID.PREPROD
})
this.bridge = new HydraBridge({
connector: new HexcoreConnector(
'wss://alpha-v1-api.hexcore.io.vn',
{ socketIoOptions: { auth: { token: jwtToken } } }
)
})
this.hydraApi = new HydraHexcoreApi()
this.setupEventHandlers()
}
private setupEventHandlers() {
this.bridge.events.on('onConnected', () => {
console.log('🔗 Hydra Bridge Connected')
})
this.bridge.events.on('onMessage', (payload) => {
if (payload.tag === 'TxValid') {
console.log('✅ Hydra TX Valid:', payload.transaction)
}
})
}
async sendHydraTransaction(recipientAddress: string, amountLovelace: string) {
try {
// 1. Connect to Hydra if not connected
if (!this.bridge.connected()) {
await this.bridge.connect()
}
// 2. Query UTxOs from Hydra
const walletAddress = this.wallet.getAccount().baseAddressBech32
const utxos = await this.hydraApi.queryAddressUTxO(walletAddress)
// 3. Build Hydra transaction
const txBuilder = new TxBuilder({
isHydra: true,
params: { minFeeA: 0, minFeeB: 0 }
})
const tx = await txBuilder
.setInputs(utxos)
.addOutput({
address: recipientAddress,
amount: [{ unit: 'lovelace', quantity: amountLovelace }]
})
.setChangeAddress(walletAddress)
.complete()
// 4. Sign transaction
const signedTx = await this.wallet.signTx(tx.to_hex())
// 5. Submit to Hydra
const txHash = await this.hydraApi.submitHydraTx(signedTx)
console.log('🚀 Hydra Transaction Submitted:', txHash)
return txHash
} catch (error) {
console.error('❌ Hydra Transaction Error:', error)
throw error
}
}
async disconnect() {
await this.bridge.disconnect()
}
}
// Usage
const hydraManager = new HydraWalletManager(
'your mnemonic words here'.split(' '),
'your-jwt-token'
)
await hydraManager.sendHydraTransaction(
'addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer',
'1000000'
)
submitTx — Callback API (v1.3.0+)
Non-blocking fire-and-forget submission using Node.js error-first callback pattern:
import { HydraBridge } from '@hydra-sdk/bridge'
const bridge = new HydraBridge({ url: 'ws://localhost:4001' })
await bridge.connect()
// submitTx returns void immediately — result delivered via callback
bridge.submitTx(
{
type: 'Witnessed Tx ConwayEra',
description: '',
cborHex: signedTxCbor,
txId: transactionId
},
(error, result) => {
if (error) {
console.error('Tx rejected:', error.reason, 'tag:', error.tag)
return
}
console.log('Confirmed in snapshot #', result!.result?.snapshot?.number)
console.log('isValid:', result!.isValid)
},
{ timeout: 30000 }
)
When to use submitTx vs submitTxSync
submitTxSync | submitTx | |
|---|---|---|
| Returns | Promise<TxResult> | void |
| Usage | await bridge.submitTxSync(tx) | callback-based |
| Best for | Sequential flows, async/await | Event loops, fire-and-forget |
In-Memory Balance Cache (v1.3.0+)
After the first Greetings or SnapshotConfirmed, balance reads are O(1):
bridge.events.on('onMessage', payload => {
if (payload.tag === 'SnapshotConfirmed') {
// Cache is updated automatically
const balance = bridge.getAddressBalance('addr_test1...')
if (balance === null) return // cold start, cache not seeded yet
const lovelace = balance.get('lovelace') ?? 0n
console.log('Balance:', Number(lovelace) / 1_000_000, 'ADA')
}
})
Common Hydra Pitfalls và Solutions
1. Wrong Transaction Mode
// ❌ Wrong - using Cardano params for Hydra
const txBuilder = new TxBuilder({}) // Default Cardano params
// ✅ Correct - using Hydra params
const txBuilder = new TxBuilder({
isHydra: true,
params: { minFeeA: 0, minFeeB: 0 }
})
2. Socket Connection Issues
// ❌ Wrong - using plain WebSocket
const ws = new WebSocket('wss://alpha-v1-api.hexcore.io.vn')
// ✅ Correct - using Socket.IO with auth
const bridge = new HydraBridge({
connector: new HexcoreConnector(url, {
socketIoOptions: {
auth: { token: 'your-jwt-token' }
}
})
})
3. Memory Leaks
// ✅ Proper cleanup
class HydraComponent {
private bridge: HydraBridge
async mounted() {
this.bridge = new HydraBridge({...})
await this.bridge.connect()
}
async beforeUnmount() {
// Important: clean up the connection
await this.bridge?.disconnect()
}
}
