Skip to main content
After creating a transaction with operations, you need to sign it with the appropriate private key(s) and broadcast it to the Hive network.

Signing with a Single Key

1

Create and prepare your transaction

import { Transaction, PrivateKey } from 'hive-tx'

const tx = new Transaction()
await tx.addOperation('transfer', {
  from: 'alice',
  to: 'bob',
  amount: '1.000 HIVE',
  memo: 'Payment'
})
2

Load your private key

// From WIF string
const key = PrivateKey.from('5JdeC9P7Pbd1uGdFVEsJ41EkEnADbbHGq6p1BwFxm6txNBsQnsw')

// Or from username/password
const key = PrivateKey.fromLogin('alice', 'mypassword', 'active')
3

Sign the transaction

tx.sign(key)
The sign() method returns the signed transaction object.
4

Broadcast to the network

const result = await tx.broadcast()
console.log('Transaction ID:', result.tx_id)
console.log('Status:', result.status) // 'unknown'

Understanding Key Types

Different operations require different key authorities:
Key TypeUsageExamples
PostingSocial operationsvote, comment, custom_json (follow, reblog)
ActiveFinancial operationstransfer, delegate, power up/down
OwnerAccount changesUpdate keys, recover account
MemoEncrypt/decrypt memosPrivate messages in transfers
Always use the minimum required key authority. Never use owner keys for daily operations.

Broadcasting Options

The broadcast() method accepts a checkStatus parameter:

Quick Broadcast (Default)

const result = await tx.broadcast()
// Returns immediately after submitting to mempool
console.log(result.tx_id)    // '0a1b2c3d...'
console.log(result.status)   // 'unknown'
Quick broadcast returns immediately but doesn’t guarantee the transaction will be included in a block. The transaction could still expire in the mempool.

Confirmed Broadcast

const result = await tx.broadcast(true)
// Waits until transaction is included or fails
console.log(result.tx_id)    // '0a1b2c3d...'
console.log(result.status)   // 'within_irreversible_block'
Possible status values:
  • within_irreversible_block - Successfully included and irreversible
  • expired_irreversible - Transaction expired before inclusion
  • too_old - Transaction is too old to be included
Use confirmed broadcast for critical operations where you need to ensure the transaction was included.

Checking Transaction Status

You can manually check transaction status after broadcasting:
import { Transaction, PrivateKey } from 'hive-tx'

const tx = new Transaction()
await tx.addOperation('transfer', {
  from: 'alice',
  to: 'bob',
  amount: '1.000 HIVE',
  memo: 'Payment'
})

const key = PrivateKey.fromLogin('alice', 'password', 'active')
tx.sign(key)

// Quick broadcast
const result = await tx.broadcast()
console.log('Submitted:', result.tx_id)

// Wait a bit for block inclusion
await new Promise(resolve => setTimeout(resolve, 3000))

// Check status
const status = await tx.checkStatus()
console.log('Current status:', status.status)
Possible status values:
  • unknown - Not found (not yet processed)
  • within_mempool - Waiting in mempool
  • within_reversible_block - Included but reversible
  • within_irreversible_block - Included and irreversible
  • expired_reversible - Expired (was in reversible block)
  • expired_irreversible - Expired (confirmed)
  • too_old - Transaction too old to track

Error Handling

Handling Broadcast Errors

import { Transaction, PrivateKey, RPCError } from 'hive-tx'

try {
  const tx = new Transaction()
  
  await tx.addOperation('transfer', {
    from: 'alice',
    to: 'bob',
    amount: '1.000 HIVE',
    memo: 'Test'
  })
  
  const key = PrivateKey.fromLogin('alice', 'password', 'active')
  tx.sign(key)
  
  const result = await tx.broadcast(true)
  
  if (result.status === 'within_irreversible_block') {
    console.log('Transaction successful:', result.tx_id)
  } else {
    console.log('Transaction failed:', result.status)
  }
  
} catch (error) {
  if (error instanceof RPCError) {
    // Blockchain rejected the transaction
    console.error('Blockchain error:', error.message)
    console.error('Error code:', error.code)
    
    // Common error messages:
    // - "missing required active authority"
    // - "Account does not have sufficient funds"
    // - "Duplicate transaction check failed"
    
  } else if (error.message.includes('no signatures')) {
    console.error('Transaction not signed')
  } else if (error.message.includes('empty transaction')) {
    console.error('No operations added')
  } else {
    console.error('Unexpected error:', error)
  }
}

Common Errors

Missing signatures: Call .sign(key) before broadcasting.
const tx = new Transaction()
await tx.addOperation('vote', { /* ... */ })
// Forgot to sign!
await tx.broadcast() // Error: "no signatures"
Wrong key authority: Make sure you’re using the correct key type for the operation.
// Using posting key for transfer (needs active key)
const key = PrivateKey.fromLogin('alice', 'password', 'posting')
tx.sign(key)
await tx.broadcast() // Error: "missing required active authority"
Insufficient funds: Check balance before broadcasting financial operations.
import { callRPC } from 'hive-tx'

// Check balance first
const accounts = await callRPC('condenser_api.get_accounts', [['alice']])
const balance = parseFloat(accounts[0].balance)

if (balance >= 1.0) {
  // Safe to transfer
  await tx.addOperation('transfer', {
    from: 'alice',
    to: 'bob',
    amount: '1.000 HIVE',
    memo: 'Payment'
  })
}

Transaction Digest

Get the transaction hash and ID before broadcasting:
const tx = new Transaction()
await tx.addOperation('vote', {
  voter: 'alice',
  author: 'bob',
  permlink: 'post',
  weight: 10000
})

const { digest, txId } = tx.digest()
console.log('Transaction ID:', txId)
console.log('Digest (hash):', digest)

// Now sign and broadcast
const key = PrivateKey.fromLogin('alice', 'password', 'posting')
tx.sign(key)
await tx.broadcast()
The digest is a SHA256 hash used for signing. The transaction ID is derived from this hash.

Duplicate Transaction Handling

The library automatically handles duplicate transaction errors:
const tx = new Transaction()
await tx.addOperation('vote', { /* ... */ })
tx.sign(key)

// First broadcast
await tx.broadcast()

// Retry (e.g., due to network error)
try {
  await tx.broadcast()
  // Duplicate error is automatically ignored
} catch (error) {
  // Other errors will still throw
}
From Transaction.ts:106-124:
try {
  await callRPC('condenser_api.broadcast_transaction', [this.transaction])
} catch (e) {
  if (e instanceof RPCError && e.message.includes('Duplicate transaction check failed')) {
    // ignore duplicate transaction error as this can happen when we retry the broadcast
  } else {
    throw e
  }
}

Complete Example

Here’s a complete example with error handling and status checking:
import { Transaction, PrivateKey, RPCError } from 'hive-tx'

async function sendTransfer() {
  try {
    // Create transaction
    const tx = new Transaction({ expiration: 120_000 })
    
    // Add operation
    await tx.addOperation('transfer', {
      from: 'alice',
      to: 'bob',
      amount: '5.000 HIVE',
      memo: 'Payment for services'
    })
    
    // Sign with active key
    const key = PrivateKey.fromLogin('alice', 'mypassword', 'active')
    tx.sign(key)
    
    // Broadcast and wait for confirmation
    console.log('Broadcasting transaction...')
    const result = await tx.broadcast(true)
    
    if (result.status === 'within_irreversible_block') {
      console.log('✓ Transfer successful!')
      console.log('Transaction ID:', result.tx_id)
      return result.tx_id
    } else {
      console.error('✗ Transfer failed:', result.status)
      return null
    }
    
  } catch (error) {
    if (error instanceof RPCError) {
      console.error('Blockchain error:', error.message)
      if (error.message.includes('sufficient funds')) {
        console.error('Insufficient balance')
      } else if (error.message.includes('authority')) {
        console.error('Wrong key used')
      }
    } else {
      console.error('Error:', error.message)
    }
    return null
  }
}

// Run it
sendTransfer()

Next Steps

Build docs developers (and LLMs) love