Skip to main content

Why transactions need signatures

Every transaction on the Hive blockchain must be cryptographically signed to prove that it’s authorized by the account(s) performing the operations. Signatures ensure:

Authentication

Proves the transaction creator has access to the private keys

Authorization

Verifies the account holder approves the operations

Integrity

Ensures the transaction hasn’t been modified after signing

Non-repudiation

Creates an immutable proof of the transaction’s origin

How signing works

Signature digest

Before signing, WAX calculates a signature digest (hash) of the transaction:
const digest = tx.sigDigest;
// Returns: "a1b2c3d4e5f6789..."

// The digest includes:
// - Chain ID
// - Transaction data (operations, expiration, TAPOS)
// - HF26 serialization format
The signature digest is deterministic - the same transaction always produces the same digest, making signatures verifiable.

Signature providers

WAX supports multiple signature providers through a pluggable interface. You choose the provider based on your application’s needs:

Available providers

Beekeeper

Server-side wallet manager for automated signing

Hive Keychain

Browser extension for secure key management

PeakVault

Mobile-friendly wallet integration

MetaMask

Ethereum wallet adapter for Hive

Provider interface

All signature providers implement a common interface:
ts/wasm/lib/detailed/extensions/signatures/index.ts
export interface IOnlineSignatureProvider {
  /**
   * Signs a transaction by signing a digest of the transaction
   *
   * @param transaction - Transaction to be signed
   * @returns Promise that resolves when signing is complete
   */
  signTransaction(transaction: ITransaction): Promise<void>;
}

// Legacy interface (deprecated)
export interface ISignatureProvider {
  signDigest(publicKey: TPublicKey, sigDigest: THexString): TSignature;
  encryptData(content: string, key: TPublicKey, anotherKey?: TPublicKey, nonce?: number): string;
  decryptData(content: string, key: TPublicKey, anotherKey?: TPublicKey): string;
}

Signing with Beekeeper

Beekeeper is the recommended signature provider for server-side applications and automation:
import { createHiveChain } from "@hiveio/wax";
import BeekeeperProvider from "@hiveio/wax-signers-beekeeper";
import { Beekeeper } from "@hiveio/beekeeper";

// Initialize Beekeeper
const beekeeper = await Beekeeper.factory();
const session = await beekeeper.createSession("my-app");
const wallet = await session.createWallet("my-wallet");

// Import key
await wallet.importKey("5JPrivateKeyHere...");

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

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

// Sign transaction
const provider = await BeekeeperProvider.for(
  chain,
  wallet,
  "alice",
  "posting"
);

await provider.signTransaction(tx);

// Broadcast
await chain.broadcast(tx);

Signing with browser extensions

For web applications, browser extensions like Hive Keychain provide secure signing without exposing private keys:
import { createHiveChain } from "@hiveio/wax";
import KeychainProvider from "@hiveio/wax-signers-keychain";

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

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

// Sign with Keychain
const provider = KeychainProvider.for("alice", "posting");
await provider.signTransaction(tx);

// Broadcast
await chain.broadcast(tx);
Browser extension providers handle user interaction (prompts, confirmations) automatically.

Authority levels

Hive accounts have multiple authority levels, each with different permissions:
Used for social interactions:
  • Voting on content
  • Creating posts and comments
  • Following/unfollowing
  • Custom JSON operations (most apps)
Lowest security level - Safe to use with third-party apps
Used for financial operations:
  • Transfers
  • Market orders
  • Power ups/downs
  • Account updates
Medium security level - Use with trusted apps only
Used for account recovery:
  • Changing owner key
  • Account recovery
  • Changing other authorities
Highest security level - Keep offline, use rarely
Used for encrypting memos:
  • Encrypting transfer memos
  • Private messages
Special purpose - Separate key for privacy

Required authorities

You can check which authorities a transaction requires:
const authorities = tx.requiredAuthorities;

console.log("Posting:", Array.from(authorities.posting));
// ["alice"]

console.log("Active:", Array.from(authorities.active));
// []

console.log("Owner:", Array.from(authorities.owner));
// []

Multiple signatures

Some operations require signatures from multiple accounts or multiple keys from the same account:
// Multi-signature transaction
const tx = await chain.createTransaction();

// Escrow operation requires both parties
tx.pushOperation({
  escrow_release_operation: {
    from: "alice",
    to: "bob",
    agent: "escrow-agent",
    who: "alice",
    receiver: "bob",
    escrow_id: 12345,
    hbd_amount: chain.hbd(100),
    hive_amount: chain.hive(0)
  }
});

// Sign with first account
const provider1 = await BeekeeperProvider.for(
  chain,
  wallet1,
  "alice",
  "active"
);
await provider1.signTransaction(tx);

// Sign with second account
const provider2 = await BeekeeperProvider.for(
  chain,
  wallet2,
  "escrow-agent",
  "active"
);
await provider2.signTransaction(tx);

console.log("Signatures:", tx.transaction.signatures.length);
// 2

Manual signature handling

For advanced use cases, you can manage signatures manually:
// Get signature digest
const digest = tx.sigDigest;

// Sign externally (e.g., hardware wallet)
const signature = await externalSigner.sign(digest);

// Add signature to transaction
tx.addSignature(signature);

// Check if transaction is signed
if (tx.isSigned()) {
  await chain.broadcast(tx);
}

Signature verification

You can verify that a transaction’s signatures are valid:
// Get public keys that signed the transaction
const signerKeys = tx.signatureKeys;
// Returns: ["STM6vJmrwaX5...", "STM5VJ9YJH7r..."]

// Verify signature matches expected key
const expectedKey = "STM6vJmrwaX5TjgTS9dPH8KsArso5m91fVodJvv91j7G765wqcNM9";
if (signerKeys.includes(expectedKey)) {
  console.log("Signature verified!");
}

Encryption and decryption

Signature providers also handle encryption for private memos:
import BeekeeperProvider from "@hiveio/wax-signers-beekeeper";

const provider = await BeekeeperProvider.for(
  chain,
  wallet,
  "alice",
  "posting"
);

// Encrypt memo
const encrypted = await provider.encryptData(
  "Secret message",
  "STM6vJmrwaX5TjgTS9dPH8KsArso5m91fVodJvv91j7G765wqcNM9"  // Recipient's memo key
);

// Transfer with encrypted memo
tx.pushOperation({
  transfer_operation: {
    from: "alice",
    to: "bob",
    amount: chain.hive(1),
    memo: encrypted  // Starts with #
  }
});

// Decrypt memo
const decrypted = await provider.decryptData(encrypted);
console.log(decrypted);  // "Secret message"
Encrypted memos must start with # to be recognized as encrypted. WAX handles this automatically when you use encryption.

Key management

WAX provides utilities for working with cryptographic keys:

Generate keys

// Generate random private key
const privateKey = wax.generatePrivateKey();
// Returns: "5JPrivateKeyHere..."

// Calculate public key from private key
const publicKey = wax.calculatePublicKey(privateKey);
// Returns: "STM6vJmrwaX5..."

// Generate from account, role, and password
const keyData = wax.generatePrivateKey(
  "alice",
  "posting",
  "my-secure-password"
);
// Returns: { privateKey: "5J...", publicKey: "STM..." }

Brain keys

const brainKey = wax.suggestBrainKey();
// Returns:
// {
//   brainKey: "WORD1 WORD2 WORD3 ...",
//   privateKey: "5J...",
//   publicKey: "STM..."
// }

Best practices

Always use signature providers that keep private keys secure. Never log or transmit private keys.
Only request the minimum authority level needed for your operations. Use posting authority for social operations.
Always call tx.validate() before signing to catch errors early and avoid wasting signatures.
Use tx.requiredAuthorities to inform users which keys they need to sign with.
Users may reject signing requests. Always handle errors and provide clear feedback.

Next steps

Broadcasting

Learn how to broadcast signed transactions

Signature Providers

Explore all signature provider options

Transactions

Review transaction concepts

Security

Best practices for secure signing

Build docs developers (and LLMs) love