Skip to main content
Transactions are the fundamental unit of interaction with the Hive blockchain. The Transaction class provides a complete lifecycle for creating, signing, and broadcasting operations.

Transaction Lifecycle

Every transaction follows this sequence:
  1. Create - Initialize a transaction with default or custom expiration
  2. Add Operations - Append one or more operations to execute
  3. Sign - Sign with one or more private keys
  4. Broadcast - Submit to the Hive network
1

Create Transaction

import { Transaction } from 'hive-tx'

// Default 60 second expiration
const tx = new Transaction()

// Custom expiration (3 minutes)
const txCustom = new Transaction({ expiration: 180_000 })
2

Add Operations

// Add a vote operation
await tx.addOperation('vote', {
voter: 'alice',
author: 'bob',
permlink: 'awesome-post',
weight: 10000  // 100% upvote
})

// Add more operations to the same transaction
await tx.addOperation('transfer', {
from: 'alice',
to: 'bob',
amount: '1.000 HIVE',
memo: 'Thanks for the post!'
})
3

Sign Transaction

import { PrivateKey } from 'hive-tx'

const key = PrivateKey.from('5JdeC9P7Pbd1uGdFVEsJ41EkEnADbbHGq6p1BwFxm6txNBsQnsw')
tx.sign(key)
4

Broadcast

// Broadcast and return immediately
const result = await tx.broadcast()
console.log(result.tx_id) // Transaction ID

// Broadcast and wait for confirmation
const confirmedResult = await tx.broadcast(true)
console.log(confirmedResult.status) // 'within_irreversible_block'

Transaction Structure

Under the hood, a Hive transaction contains these fields:
expiration
string
required
ISO 8601 timestamp when the transaction expires (max 24 hours from creation)
ref_block_num
number
required
Lower 16 bits of reference block number (prevents replay attacks)
ref_block_prefix
number
required
Reference block prefix derived from head block ID
operations
Operation[]
required
Array of operations to execute, each formatted as [operationName, operationBody]
extensions
Extension[]
default:"[]"
Optional protocol extensions (rarely used)
signatures
string[]
required
Array of hex-encoded signatures (added when you call sign())
The ref_block_num and ref_block_prefix are automatically set from the current head block when you call addOperation(). This ties your transaction to recent blockchain state and prevents replay attacks.

Creating Transactions

You can create a transaction in two ways:

With Default Expiration

By default, transactions expire 60 seconds after creation:
const tx = new Transaction()
// Expiration: 60_000ms (60 seconds)

With Custom Expiration

Set a custom expiration in milliseconds (max 86400000ms = 24 hours):
// 5 minute expiration
const tx = new Transaction({ expiration: 300_000 })

// 1 hour expiration
const tx = new Transaction({ expiration: 3_600_000 })
Transactions expire after the specified time. If broadcast fails or is delayed, the transaction becomes invalid after expiration.

Transaction Digest and ID

The transaction digest is a SHA256 hash used for signing. The transaction ID is derived from this digest.

Getting the Digest

src/Transaction.ts:153
const { digest, txId } = tx.digest()

console.log(txId)    // First 40 hex characters of SHA256(transaction)
console.log(digest)  // SHA256(chainId + transaction) as Uint8Array
The digest combines:
  • Chain ID - Hive mainnet identifier (beeab0de...)
  • Serialized Transaction - Binary representation of the transaction
This ensures signatures are valid only for the specific transaction on the specific chain.

Multi-Signature Transactions

Sign with multiple keys by passing an array or calling sign() multiple times:

Sign All at Once

const key1 = PrivateKey.from('5JdeC9P7Pbd1uGdFVEsJ41Ek...')
const key2 = PrivateKey.from('5HqNQNxcfq7Y84fLk8RVpC...')

tx.sign([key1, key2])

Sign Incrementally

// Sign with first key
tx.sign(activeKey)

// Later, sign with second key
tx.sign(postingKey)

// Both signatures are added to transaction.signatures[]
Use incremental signing when keys are held by different parties or when you need to sign at different times.

Broadcasting Transactions

The broadcast() method submits your signed transaction to the Hive network.

Quick Broadcast

Return immediately after submission (status may be unknown):
src/Transaction.ts:106
const result = await tx.broadcast()
// { tx_id: '...', status: 'unknown' }

Confirmed Broadcast

Wait for the transaction to be included in an irreversible block:
const result = await tx.broadcast(true)
// { tx_id: '...', status: 'within_irreversible_block' }
Possible status values:
  • unknown - Broadcast succeeded but status not checked
  • within_mempool - Waiting in transaction pool
  • within_reversible_block - In a block but not yet irreversible
  • within_irreversible_block - Permanently confirmed
  • expired_reversible - Expired before becoming irreversible
  • expired_irreversible - Expired after being irreversible
  • too_old - Transaction is too old to check
Broadcasting with checkStatus=false does not guarantee the transaction will be included in a block. It may expire in the mempool.

Checking Transaction Status

Manually check a transaction’s status using checkStatus():
src/Transaction.ts:193
await tx.broadcast()

// Check status later
const status = await tx.checkStatus()
console.log(status.status) // Current transaction status

Adding External Signatures

If you sign transactions with external tools (hardware wallets, etc.), add signatures manually:
src/Transaction.ts:178
const signature = '1f4c3d2e...' // 130-character hex string
tx.addSignature(signature)
Signatures must be exactly 130 hex characters (65 bytes). This includes the recovery byte and the 64-byte ECDSA signature.

Error Handling

Common errors and solutions:

No Operations

try {
  await tx.broadcast()
} catch (e) {
  // Error: Attempted to broadcast an empty transaction
  // Solution: Call addOperation() first
}

No Signatures

try {
  await tx.broadcast()
} catch (e) {
  // Error: Attempted to broadcast a transaction with no signatures
  // Solution: Call sign(key) before broadcasting
}

Duplicate Transaction

The SDK automatically handles duplicate transaction errors when retrying broadcasts.

Complete Example

Here’s a complete transaction workflow:
import { Transaction, PrivateKey } from 'hive-tx'

async function publishPost() {
  // Create transaction with 5 minute expiration
  const tx = new Transaction({ expiration: 300_000 })
  
  // Add comment operation
  await tx.addOperation('comment', {
    parent_author: '',
    parent_permlink: 'hive',
    author: 'alice',
    permlink: 'my-first-post',
    title: 'Hello Hive!',
    body: 'This is my first post on Hive.',
    json_metadata: JSON.stringify({ tags: ['hive', 'introduction'] })
  })
  
  // Add comment options for beneficiaries
  await tx.addOperation('comment_options', {
    author: 'alice',
    permlink: 'my-first-post',
    max_accepted_payout: '1000000.000 HBD',
    percent_hbd: 10000,
    allow_votes: true,
    allow_curation_rewards: true,
    extensions: [[
      { account: 'bob', weight: 2500 } // 25% beneficiary
    ]]
  })
  
  // Sign with posting key
  const postingKey = PrivateKey.fromLogin('alice', 'password', 'posting')
  tx.sign(postingKey)
  
  // Broadcast and wait for confirmation
  const result = await tx.broadcast(true)
  console.log(`Post published! TX ID: ${result.tx_id}`)
  console.log(`Status: ${result.status}`)
}

publishPost()

Next Steps

Operations

Learn about all available Hive operations

Keys & Signatures

Understand cryptographic operations

Transaction API

Full API reference for Transaction class

Voting Guide

Build a voting application

Build docs developers (and LLMs) love