Skip to main content

What is a transaction

A transaction is a signed, atomic unit of work that you submit to the Hive blockchain. Each transaction contains one or more operations that modify the blockchain state.
All operations within a transaction either succeed together or fail together. This ensures data consistency across the blockchain.

Transaction lifecycle

Creating transactions

You create transactions using the chain instance or foundation instance, depending on whether you need online or offline functionality.
import { createHiveChain } from "@hiveio/wax";

const chain = await createHiveChain();

// Online transaction (automatically fetches TAPOS data)
const tx = await chain.createTransaction();

// Offline transaction (requires manual TAPOS block ID)
import { createWaxFoundation } from "@hiveio/wax";
const wax = await createWaxFoundation();

const tx = wax.createTransaction({
  taposBlockId: "0000abcd..."
});

Transaction options

You can customize transaction creation with several options:
taposBlockId
string
Block ID for TAPOS (Transaction as Proof of Stake) reference
expirationTime
string | timedelta
default:"+1m"
When the transaction expires (e.g., "+1m", "+5m", "+1h")
chainId
string
default:"mainnet"
Chain ID for the target blockchain (mainnet, testnet, etc.)
headBlockTime
Date | HiveDateTime
Reference time for expiration calculation (useful for testnets)

TAPOS (Transaction as Proof of Stake)

TAPOS ensures your transaction references a recent block, preventing replay attacks across different blockchain forks.

How TAPOS works

Every transaction includes:
  • ref_block_num: Lower 16 bits of the referenced block number
  • ref_block_prefix: First 32 bits of the referenced block ID
These values are automatically calculated from the block ID:
ts/wasm/lib/detailed/transaction.ts
private taposRefer(hex: TBlockHash): {
  ref_block_num: number;
  ref_block_prefix: number
} {
  return this.api.wasmManager.safeWasmCall(() =>
    this.api.protocol.cpp_get_tapos_data(hex)
  );
}

Building transactions

You build transactions by adding operations using the pushOperation() method:
import { createHiveChain } from "@hiveio/wax";

const chain = await createHiveChain();
const tx = await chain.createTransaction();

// Add a vote operation
tx.pushOperation({
  vote_operation: {
    voter: "alice",
    author: "bob",
    permlink: "example-post",
    weight: 10000
  }
});

// Add a transfer operation
tx.pushOperation({
  transfer_operation: {
    from: "alice",
    to: "bob",
    amount: chain.hive(1),
    memo: "Payment"
  }
});

// Chain multiple operations
tx.pushOperation({ /* operation 1 */ })
  .pushOperation({ /* operation 2 */ })
  .pushOperation({ /* operation 3 */ });

Complex operations

WAX provides high-level operation builders for complex operations:
import { CommentBuilder } from "@hiveio/wax";

const comment = new CommentBuilder()
  .withAuthor("alice")
  .withPermlink("my-post")
  .withTitle("Hello Hive")
  .withBody("This is my first post!")
  .withTags(["introduction", "hive"]);

tx.pushOperation(comment);

Transaction structure

At the protocol level, a transaction has the following structure:
interface transaction {
  ref_block_num: number;       // TAPOS reference
  ref_block_prefix: number;    // TAPOS reference
  expiration: string;          // ISO 8601 timestamp
  operations: operation[];     // Array of operations
  extensions: any[];           // Future extensions
  signatures: string[];        // Digital signatures
}

Example transaction

{
  "ref_block_num": 12345,
  "ref_block_prefix": 987654321,
  "expiration": "2026-03-04T12:00:00",
  "operations": [
    {
      "vote_operation": {
        "voter": "alice",
        "author": "bob",
        "permlink": "example-post",
        "weight": 10000
      }
    }
  ],
  "extensions": [],
  "signatures": [
    "1f3a5b..."
  ]
}

Validation

Before signing, you can (and should) validate your transaction:
try {
  tx.validate();
  console.log("Transaction is valid!");
} catch (error) {
  console.error("Validation error:", error.message);
}
Validation is automatically performed when you sign a transaction, but it’s good practice to validate early to catch errors.

What validation checks

The validation process verifies:
  • Operation structure matches protocol definitions
  • Required fields are present
  • Field values are within allowed ranges
  • Account names follow Hive naming rules
  • Assets have correct format and precision
  • Custom JSON is valid

Transaction properties

Once you build a transaction, you can access various properties:

Transaction ID

const txId = tx.id;
// Returns: "a1b2c3d4e5f6..."

// For legacy compatibility
const legacyId = tx.legacy_id;

Signature digest

const digest = tx.sigDigest;
// Used for signing

Impacted accounts

const accounts = tx.impactedAccounts;
// Returns: Set<string> { "alice", "bob" }

Required authorities

const authorities = tx.requiredAuthorities;
// Returns:
// {
//   posting: Set<string>,
//   active: Set<string>,
//   owner: Set<string>,
//   other: Array<authority>
// }

Transaction serialization

You can serialize transactions to different formats:

JSON API format

const json = tx.toApiJson();
// Returns transaction in API JSON format

const jsonString = tx.toString();
// Returns stringified JSON

Binary format

const binary = tx.toBinaryForm();
// Returns hex string

// Strip signatures (for partial signing)
const unsignedBinary = tx.toBinaryForm(true);

Legacy format

const legacy = tx.toLegacyApi();
// For compatibility with older APIs

Loading existing transactions

You can load and work with existing transactions:
import { Transaction } from "@hiveio/wax";

// From JSON string
const tx = Transaction.fromApi(wax, jsonString);

// From JSON object
const tx = Transaction.fromApi(wax, jsonObject);

// From proto transaction
const tx = new Transaction(wax, {
  protoTransaction: protoTx
});

Expiration handling

Transactions have an expiration time to prevent replay attacks:

Default expiration

// Default: +1 minute from now
const tx = await chain.createTransaction();

// Custom expiration
const tx = await chain.createTransaction({
  expirationTime: "+5m"  // 5 minutes
});

Expiration format

The expiration is stored as an ISO 8601 timestamp without milliseconds:
2026-03-04T12:34:56
Once a transaction expires, it cannot be broadcast. Make sure to sign and broadcast before expiration.

Implementation details

The transaction implementation resides in:
  • TypeScript: ts/wasm/lib/detailed/transaction.ts:48
  • Python: python/wax/_private/transaction.py:51
  • C++ Core: core/foundation.cpp (handle management)

Transaction handle

Internally, both implementations maintain a handle to the C++ transaction object:
ts/wasm/lib/detailed/transaction.ts
protected txHandle: transaction_handle;

// Created via WASM call
this.txHandle = api.wasmManager.safeWasmCall(() =>
  api.protocol.cpp_create_transaction_handle(this.target, true)
);

Next steps

Operations

Learn about operations and protocol buffers

Signing

Understand how to sign transactions

Broadcasting

Learn how to broadcast transactions

API Reference

Explore the complete API

Build docs developers (and LLMs) love