Skip to main content
Complex operations are high-level wrappers that simplify creating sophisticated blockchain operations. They handle the complexity of building protocol buffer messages and provide intuitive APIs.

What are complex operations?

Complex operations extend the base OperationBase class and provide:
  • Type-safe APIs - IntelliSense and autocomplete support
  • Automatic validation - Built-in checks for valid data
  • Simplified workflows - Handle common patterns automatically
  • Protocol compliance - Ensure operations match blockchain requirements
Complex operations implement a finalize() method that converts high-level data into protocol buffer operations when you push them to a transaction.

Account update operations

Update account authorities (owner, active, posting) and memo keys.
import { createHiveChain } from '@hiveio/wax';
import { AccountAuthorityUpdateOperation } from '@hiveio/wax';

async function updateAccount() {
  const chain = await createHiveChain();

  // Create operation - automatically retrieves current account data
  const accountUpdate = await AccountAuthorityUpdateOperation.createFor(
    chain,
    "alice"
  );

  // Update posting authority - add a new account
  accountUpdate.roles.posting.add("bob", 1);

  // Update active authority - add a new key
  accountUpdate.roles.active.add(
    "STM7Q2rLBqzPzFeteQZewv9Lu3NLE69fZoLeL6YK59t7UmssCBNTU",
    1
  );

  // Update memo key
  accountUpdate.roles.memo.set(
    "STM7Q2rLBqzPzFeteQZewv9Lu3NLE69fZoLeL6YK59t7UmssCBNTU"
  );

  // Add to transaction
  const tx = await chain.createTransaction();
  tx.pushOperation(accountUpdate);

  console.log('Account update ready to sign');
}
The create_for() method automatically fetches current account authorities from the blockchain, making it easy to modify existing permissions.

Working with authority categories

You can iterate over authority categories or access roles directly.
// Iterate over categories
for (const role of accountUpdate.categories.hive) {
  if (role.level === 'active') {
    role.add('newaccount', 1);
  }
}

// Or access directly (recommended - better IntelliSense)
accountUpdate.roles.owner.add('newaccount', 1);
accountUpdate.roles.active.add('anotherkey', 1);
accountUpdate.roles.posting.add('bob', 1);
accountUpdate.roles.memo.set('STM...');

Recurrent transfer operations

Create recurring transfers that execute automatically.
import { createHiveChain } from '@hiveio/wax';
import { 
  DefineRecurrentTransferOperation,
  RecurrentTransferRemovalOperation,
  RecurrentTransferData
} from '@hiveio/wax';

async function setupRecurrentTransfer() {
  const chain = await createHiveChain();

  // Define a recurrent transfer
  const transferData: RecurrentTransferData = {
    fromAccount: "alice",
    toAccount: "bob",
    amount: chain.hive.satoshis(10), // 10 HIVE
    executions: 12, // Execute 12 times
    recurrence: 24, // Every 24 hours
    memo: "Monthly payment",
    pairId: undefined // Optional: for multiple transfers to same account
  };

  const recurrentTransfer = new DefineRecurrentTransferOperation(
    transferData
  );

  // Add to transaction
  const tx = await chain.createTransaction();
  tx.pushOperation(recurrentTransfer);

  console.log('Recurrent transfer configured');
}

async function cancelRecurrentTransfer() {
  const chain = await createHiveChain();

  // Remove a recurrent transfer
  const removal = new RecurrentTransferRemovalOperation(
    "alice",  // from
    "bob",    // to
    undefined // pairId (if you have multiple)
  );

  const tx = await chain.createTransaction();
  tx.pushOperation(removal);

  console.log('Recurrent transfer cancelled');
}
  • Minimum recurrence is 24 hours
  • Minimum executions is 2
  • Maximum execution date is 730 days in the future
  • Since HF28, you can have multiple recurrent transfers to the same account using pair_id

Comment and blog post operations

Create posts and comments with rich metadata.
import { createHiveChain } from '@hiveio/wax';
import { BlogPostOperation, ReplyOperation } from '@hiveio/wax';

async function createBlogPost() {
  const chain = await createHiveChain();

  // Create a blog post
  const post = new BlogPostOperation({
    author: "alice",
    body: "This is my first post using WAX!",
    title: "Hello Hive",
    category: "introduction", // Main tag/community
    tags: ["wax", "blockchain", "hive"],
    images: ["https://example.com/image.jpg"],
    description: "My introduction to Hive",
    beneficiaries: [
      { account: "bob", weight: 500 }  // 5% to bob
    ],
    allowVotes: true,
    allowCurationRewards: true,
    percentHbd: 10000, // 100% HBD
    maxAcceptedPayout: 1000000000 // 1000 HBD
  });

  // Add custom metadata
  post.pushMetadataProperty("custom_field", "custom_value");

  const tx = await chain.createTransaction();
  tx.pushOperation(post);

  console.log('Blog post ready to publish');
}

async function createReply() {
  const chain = await createHiveChain();

  // Reply to a post or comment
  const reply = new ReplyOperation({
    parentAuthor: "bob",
    parentPermlink: "hello-hive",
    author: "alice",
    body: "Great post! Welcome to Hive.",
    // Optional fields
    title: "",
    permlink: `re-bob-${Date.now()}`,
    jsonMetadata: {}
  });

  const tx = await chain.createTransaction();
  tx.pushOperation(reply);

  console.log('Reply ready to post');
}
The BlogPostOperation automatically generates metadata in the correct format and can create comment_options operations for beneficiaries and payout settings.

Witness operations

Update witness properties for block production.
import { createHiveChain } from '@hiveio/wax';
import { WitnessSetProperties, WitnessSetPropertiesData } from '@hiveio/wax';

async function updateWitnessProperties() {
  const chain = await createHiveChain();

  const witnessData: WitnessSetPropertiesData = {
    owner: "alice",
    witnessSigningKey: "STM7Q2rLBqzPzFeteQZewv9Lu3NLE69fZoLeL6YK59t7UmssCBNTU",
    newSigningKey: "STM8Q2rLBqzPzFeteQZewv9Lu3NLE69fZoLeL6YK59t7UmssCBNTU",
    accountCreationFee: chain.hive.satoshis(3), // 3 HIVE
    url: "https://mywitness.com",
    hbdExchangeRate: {
      base: chain.hbd.satoshis(100), // 1 HBD
      quote: chain.hive.satoshis(300) // = 0.33 HIVE
    },
    maximumBlockSize: 65536,
    hbdInterestRate: 1500, // 15%
    accountSubsidyBudget: 50000,
    accountSubsidyDecay: 330782
  };

  const witnessOp = new WitnessSetProperties(witnessData);

  const tx = await chain.createTransaction();
  tx.pushOperation(witnessOp);

  console.log('Witness properties ready to update');
}

Proposal voting operations

Vote on Hive DAO proposals.
import { createHiveChain } from '@hiveio/wax';
import { UpdateProposalVoteOperation } from '@hiveio/wax';

async function voteOnProposal() {
  const chain = await createHiveChain();

  // Vote for a proposal
  const vote = new UpdateProposalVoteOperation(
    "alice",      // voter
    [123, 456],   // proposal IDs
    true          // approve (false to remove vote)
  );

  const tx = await chain.createTransaction();
  tx.pushOperation(vote);

  console.log('Proposal vote ready');
}

Creating custom complex operations

You can create your own complex operation classes for application-specific workflows.
import { OperationBase, IOperationSink, operation } from '@hiveio/wax';

// Example: Splinterlands game operation
export class SplinterlandsGame extends OperationBase {
  private readonly operations: operation[] = [];

  public constructor(
    public readonly player: string
  ) {
    super();
  }

  public finalize(_sink: IOperationSink): Iterable<operation> {
    return this.operations;
  }

  public enterTournament(tournamentId: string): this {
    this.operations.push({
      custom_json_operation: {
        id: "sm_enter_tournament",
        json: JSON.stringify({
          tournament_id: tournamentId,
          app: "splinterlands/0.7.139"
        }),
        required_auths: [],
        required_posting_auths: [this.player]
      }
    });
    return this;
  }

  public stakeTokens(amount: number): this {
    this.operations.push({
      custom_json_operation: {
        id: "sm_stake_tokens",
        json: JSON.stringify({
          qty: amount.toFixed(3),
          token: "SPS",
          app: "splinterlands/0.7.139"
        }),
        required_auths: [],
        required_posting_auths: [this.player]
      }
    });
    return this;
  }
}

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

const game = new SplinterlandsGame("username");
game.enterTournament("abc123").stakeTokens(10);

tx.pushOperation(game);
Custom operations are perfect for:
  • Application-specific workflows
  • Batching related operations
  • Encapsulating business logic
  • Building reusable components

Best practices

Instead of manually constructing protocol buffer messages, use complex operations for common tasks like account updates and recurrent transfers.
// ✅ Good - Using complex operation
const accountUpdate = await AccountAuthorityUpdateOperation.createFor(
  chain,
  "alice"
);
accountUpdate.roles.posting.add("bob", 1);

// ❌ Avoid - Manual construction (more error-prone)
tx.pushOperation({
  account_update_operation: {
    account: "alice",
    posting: { /* complex authority structure */ }
  }
});
Check that your data meets requirements before creating complex operations.
// Validate recurrent transfer data
if (executions < 2) {
  throw new Error('Executions must be at least 2');
}
if (recurrence < 24) {
  throw new Error('Recurrence must be at least 24 hours');
}
Many complex operations support method chaining for cleaner code.
game
  .enterTournament("abc123")
  .stakeTokens(10)
  .claimRewards();
The finalize() method can throw errors if data is invalid. Handle these appropriately.
try {
  tx.pushOperation(complexOperation);
} catch (error) {
  console.error('Operation finalization failed:', error);
}

Next steps

API extensions

Extend the API with custom endpoints

Formatters

Format output data for display

Operations reference

Complete operations API documentation

Protocol documentation

Hive protocol specifications

Build docs developers (and LLMs) love