> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/mahdiyari/hive-tx/llms.txt
> Use this file to discover all available pages before exploring further.

# Signing and Broadcasting

> Complete guide to signing transactions and broadcasting them to the Hive network

After creating a transaction with operations, you need to sign it with the appropriate private key(s) and broadcast it to the Hive network.

## Signing with a Single Key

<Steps>
  <Step title="Create and prepare your transaction">
    ```typescript theme={null}
    import { Transaction, PrivateKey } from 'hive-tx'

    const tx = new Transaction()
    await tx.addOperation('transfer', {
      from: 'alice',
      to: 'bob',
      amount: '1.000 HIVE',
      memo: 'Payment'
    })
    ```
  </Step>

  <Step title="Load your private key">
    ```typescript theme={null}
    // From WIF string
    const key = PrivateKey.from('5JdeC9P7Pbd1uGdFVEsJ41EkEnADbbHGq6p1BwFxm6txNBsQnsw')

    // Or from username/password
    const key = PrivateKey.fromLogin('alice', 'mypassword', 'active')
    ```
  </Step>

  <Step title="Sign the transaction">
    ```typescript theme={null}
    tx.sign(key)
    ```

    The `sign()` method returns the signed transaction object.
  </Step>

  <Step title="Broadcast to the network">
    ```typescript theme={null}
    const result = await tx.broadcast()
    console.log('Transaction ID:', result.tx_id)
    console.log('Status:', result.status) // 'unknown'
    ```
  </Step>
</Steps>

## Understanding Key Types

Different operations require different key authorities:

| Key Type    | Usage                 | Examples                                     |
| ----------- | --------------------- | -------------------------------------------- |
| **Posting** | Social operations     | vote, comment, custom\_json (follow, reblog) |
| **Active**  | Financial operations  | transfer, delegate, power up/down            |
| **Owner**   | Account changes       | Update keys, recover account                 |
| **Memo**    | Encrypt/decrypt memos | Private messages in transfers                |

<Warning>
  Always use the minimum required key authority. Never use owner keys for daily operations.
</Warning>

## Broadcasting Options

The `broadcast()` method accepts a `checkStatus` parameter:

### Quick Broadcast (Default)

```typescript theme={null}
const result = await tx.broadcast()
// Returns immediately after submitting to mempool
console.log(result.tx_id)    // '0a1b2c3d...'
console.log(result.status)   // 'unknown'
```

<Note>
  Quick broadcast returns immediately but doesn't guarantee the transaction will be included in a block. The transaction could still expire in the mempool.
</Note>

### Confirmed Broadcast

```typescript theme={null}
const result = await tx.broadcast(true)
// Waits until transaction is included or fails
console.log(result.tx_id)    // '0a1b2c3d...'
console.log(result.status)   // 'within_irreversible_block'
```

Possible status values:

* `within_irreversible_block` - Successfully included and irreversible
* `expired_irreversible` - Transaction expired before inclusion
* `too_old` - Transaction is too old to be included

<Tip>
  Use confirmed broadcast for critical operations where you need to ensure the transaction was included.
</Tip>

## Checking Transaction Status

You can manually check transaction status after broadcasting:

```typescript theme={null}
import { Transaction, PrivateKey } from 'hive-tx'

const tx = new Transaction()
await tx.addOperation('transfer', {
  from: 'alice',
  to: 'bob',
  amount: '1.000 HIVE',
  memo: 'Payment'
})

const key = PrivateKey.fromLogin('alice', 'password', 'active')
tx.sign(key)

// Quick broadcast
const result = await tx.broadcast()
console.log('Submitted:', result.tx_id)

// Wait a bit for block inclusion
await new Promise(resolve => setTimeout(resolve, 3000))

// Check status
const status = await tx.checkStatus()
console.log('Current status:', status.status)
```

Possible status values:

* `unknown` - Not found (not yet processed)
* `within_mempool` - Waiting in mempool
* `within_reversible_block` - Included but reversible
* `within_irreversible_block` - Included and irreversible
* `expired_reversible` - Expired (was in reversible block)
* `expired_irreversible` - Expired (confirmed)
* `too_old` - Transaction too old to track

## Error Handling

### Handling Broadcast Errors

```typescript theme={null}
import { Transaction, PrivateKey, RPCError } from 'hive-tx'

try {
  const tx = new Transaction()
  
  await tx.addOperation('transfer', {
    from: 'alice',
    to: 'bob',
    amount: '1.000 HIVE',
    memo: 'Test'
  })
  
  const key = PrivateKey.fromLogin('alice', 'password', 'active')
  tx.sign(key)
  
  const result = await tx.broadcast(true)
  
  if (result.status === 'within_irreversible_block') {
    console.log('Transaction successful:', result.tx_id)
  } else {
    console.log('Transaction failed:', result.status)
  }
  
} catch (error) {
  if (error instanceof RPCError) {
    // Blockchain rejected the transaction
    console.error('Blockchain error:', error.message)
    console.error('Error code:', error.code)
    
    // Common error messages:
    // - "missing required active authority"
    // - "Account does not have sufficient funds"
    // - "Duplicate transaction check failed"
    
  } else if (error.message.includes('no signatures')) {
    console.error('Transaction not signed')
  } else if (error.message.includes('empty transaction')) {
    console.error('No operations added')
  } else {
    console.error('Unexpected error:', error)
  }
}
```

### Common Errors

<Warning>
  **Missing signatures**: Call `.sign(key)` before broadcasting.
</Warning>

```typescript theme={null}
const tx = new Transaction()
await tx.addOperation('vote', { /* ... */ })
// Forgot to sign!
await tx.broadcast() // Error: "no signatures"
```

<Warning>
  **Wrong key authority**: Make sure you're using the correct key type for the operation.
</Warning>

```typescript theme={null}
// Using posting key for transfer (needs active key)
const key = PrivateKey.fromLogin('alice', 'password', 'posting')
tx.sign(key)
await tx.broadcast() // Error: "missing required active authority"
```

<Warning>
  **Insufficient funds**: Check balance before broadcasting financial operations.
</Warning>

```typescript theme={null}
import { callRPC } from 'hive-tx'

// Check balance first
const accounts = await callRPC('condenser_api.get_accounts', [['alice']])
const balance = parseFloat(accounts[0].balance)

if (balance >= 1.0) {
  // Safe to transfer
  await tx.addOperation('transfer', {
    from: 'alice',
    to: 'bob',
    amount: '1.000 HIVE',
    memo: 'Payment'
  })
}
```

## Transaction Digest

Get the transaction hash and ID before broadcasting:

```typescript theme={null}
const tx = new Transaction()
await tx.addOperation('vote', {
  voter: 'alice',
  author: 'bob',
  permlink: 'post',
  weight: 10000
})

const { digest, txId } = tx.digest()
console.log('Transaction ID:', txId)
console.log('Digest (hash):', digest)

// Now sign and broadcast
const key = PrivateKey.fromLogin('alice', 'password', 'posting')
tx.sign(key)
await tx.broadcast()
```

<Note>
  The digest is a SHA256 hash used for signing. The transaction ID is derived from this hash.
</Note>

## Duplicate Transaction Handling

The library automatically handles duplicate transaction errors:

```typescript theme={null}
const tx = new Transaction()
await tx.addOperation('vote', { /* ... */ })
tx.sign(key)

// First broadcast
await tx.broadcast()

// Retry (e.g., due to network error)
try {
  await tx.broadcast()
  // Duplicate error is automatically ignored
} catch (error) {
  // Other errors will still throw
}
```

From Transaction.ts:106-124:

```typescript theme={null}
try {
  await callRPC('condenser_api.broadcast_transaction', [this.transaction])
} catch (e) {
  if (e instanceof RPCError && e.message.includes('Duplicate transaction check failed')) {
    // ignore duplicate transaction error as this can happen when we retry the broadcast
  } else {
    throw e
  }
}
```

## Complete Example

Here's a complete example with error handling and status checking:

```typescript theme={null}
import { Transaction, PrivateKey, RPCError } from 'hive-tx'

async function sendTransfer() {
  try {
    // Create transaction
    const tx = new Transaction({ expiration: 120_000 })
    
    // Add operation
    await tx.addOperation('transfer', {
      from: 'alice',
      to: 'bob',
      amount: '5.000 HIVE',
      memo: 'Payment for services'
    })
    
    // Sign with active key
    const key = PrivateKey.fromLogin('alice', 'mypassword', 'active')
    tx.sign(key)
    
    // Broadcast and wait for confirmation
    console.log('Broadcasting transaction...')
    const result = await tx.broadcast(true)
    
    if (result.status === 'within_irreversible_block') {
      console.log('✓ Transfer successful!')
      console.log('Transaction ID:', result.tx_id)
      return result.tx_id
    } else {
      console.error('✗ Transfer failed:', result.status)
      return null
    }
    
  } catch (error) {
    if (error instanceof RPCError) {
      console.error('Blockchain error:', error.message)
      if (error.message.includes('sufficient funds')) {
        console.error('Insufficient balance')
      } else if (error.message.includes('authority')) {
        console.error('Wrong key used')
      }
    } else {
      console.error('Error:', error.message)
    }
    return null
  }
}

// Run it
sendTransfer()
```

## Next Steps

* Learn about [Key Management](/guides/key-management) best practices
* Set up [Multi-signature](/guides/multi-signature) transactions
* Explore [API Calls](/guides/api-calls) for querying blockchain data
