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
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'
})
Load your private key
// From WIF string
const key = PrivateKey.from('5JdeC9P7Pbd1uGdFVEsJ41EkEnADbbHGq6p1BwFxm6txNBsQnsw')
// Or from username/password
const key = PrivateKey.fromLogin('alice', 'mypassword', 'active')
Sign the transaction
The sign() method returns the signed transaction object. 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 Type | Usage | Examples |
|---|
| Posting | Social operations | vote, comment, custom_json (follow, reblog) |
| Active | Financial operations | transfer, delegate, power up/down |
| Owner | Account changes | Update keys, recover account |
| Memo | Encrypt/decrypt memos | Private 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