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..."
});
from wax import create_hive_chain, create_wax_foundation
# Online transaction (automatically fetches TAPOS data)
chain = create_hive_chain()
tx = await chain.create_transaction()
# Offline transaction (requires manual TAPOS block ID)
wax = create_wax_foundation()
tx = wax.create_transaction( tapos_block_id = "0000abcd..." )
Transaction options
You can customize transaction creation with several options:
Block ID for TAPOS (Transaction as Proof of Stake) reference
expirationTime
string | timedelta
default: "+1m"
When the transaction expires (e.g., "+1m", "+5m", "+1h")
Chain ID for the target blockchain (mainnet, testnet, etc.)
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 )
);
}
python/wax/_private/transaction.py
from wax._private.cython_wrappers import get_tapos_data
tapos = get_tapos_data(tapos_block_id)
# Returns: python_ref_block_data(
# ref_block_num=...,
# ref_block_prefix=...
# )
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 */ });
from wax import create_hive_chain
from wax.proto.operations import vote, transfer
chain = create_hive_chain()
tx = await chain.create_transaction()
# Add a vote operation
tx.push_operation(
vote(
voter = "alice" ,
author = "bob" ,
permlink = "example-post" ,
weight = 10000
)
)
# Add a transfer operation
tx.push_operation(
transfer(
from_account = "alice" ,
to_account = "bob" ,
amount = chain.hive( 1 ),
memo = "Payment"
)
)
# Chain multiple operations
tx.push_operation(op1).push_operation(op2).push_operation(op3)
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 );
from wax.complex_operations import CommentBuilder
comment = (
CommentBuilder()
.with_author( "alice" )
.with_permlink( "my-post" )
.with_title( "Hello Hive" )
.with_body( "This is my first post!" )
.with_tags([ "introduction" , "hive" ])
)
tx.push_operation(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 );
}
try :
tx.validate()
print ( "Transaction is valid!" )
except Exception as error:
print ( f "Validation error: { error } " )
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 ;
tx_id = tx.id
# Returns: "a1b2c3d4e5f6..."
Signature digest
const digest = tx . sigDigest ;
// Used for signing
digest = tx.sig_digest
# Used for signing
Impacted accounts
const accounts = tx . impactedAccounts ;
// Returns: Set<string> { "alice", "bob" }
accounts = tx.impacted_accounts
# Returns: ["alice", "bob"]
Required authorities
const authorities = tx . requiredAuthorities ;
// Returns:
// {
// posting: Set<string>,
// active: Set<string>,
// owner: Set<string>,
// other: Array<authority>
// }
authorities = tx.required_authorities
# Returns TransactionRequiredAuthorities with:
# - posting: list[str]
# - active: list[str]
# - owner: list[str]
# - other: list[authority]
Transaction serialization
You can serialize transactions to different formats:
const json = tx . toApiJson ();
// Returns transaction in API JSON format
const jsonString = tx . toString ();
// Returns stringified JSON
json_str = tx.to_api()
# Returns JSON string
json_dict = tx.to_dict()
# Returns dictionary
const binary = tx . toBinaryForm ();
// Returns hex string
// Strip signatures (for partial signing)
const unsignedBinary = tx . toBinaryForm ( true );
binary = tx.to_binary_form()
# Returns hex string
const legacy = tx . toLegacyApi ();
// For compatibility with older APIs
legacy = tx.to_legacy_api()
# 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
});
from wax._private.transaction import Transaction
# From JSON string or dict
tx = Transaction.from_api(wax, json_data)
# From proto transaction
tx = Transaction(wax, proto_tx)
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
});
from datetime import timedelta
# Default: +1 minute from now
tx = await chain.create_transaction()
# Custom expiration
tx = await chain.create_transaction(
expiration_time = timedelta( minutes = 5 )
)
The expiration is stored as an ISO 8601 timestamp without milliseconds:
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 )
);
python/wax/_private/transaction.py
self ._handle = create_wax_transaction(
self ._target,
is_protobuf = 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