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

submitTxSyncsubmitTx
ReturnsPromise<TxResult>void
Usageawait bridge.submitTxSync(tx)callback-based
Best forSequential flows, async/awaitEvent 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()
  }
}