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.
Overview
The Memo utility provides AES encryption and decryption for private messages between Hive users. It uses ECDH (Elliptic Curve Diffie-Hellman) key exchange to derive shared secrets and AES encryption for secure communication.
Messages must start with # to be encrypted/decrypted. Plain text messages (without #) are returned unchanged.
Methods
encode
Encrypts a memo using AES encryption for secure private messaging on Hive.
const encryptedMemo = Memo.encode(privateKey, publicKey, memo, testNonce?)
privateKey
string | PrivateKey
required
Sender’s private memo key (WIF string or PrivateKey instance)
publicKey
string | PublicKey
required
Recipient’s public memo key (string or PublicKey instance)
Message to encrypt. Must start with # for encryption. Plain text (without #) is returned unchanged.
Optional nonce for testing purposes (advanced usage)
Encrypted memo string prefixed with #, or original string if not encrypted
Example
import { Memo, PrivateKey, PublicKey } from 'hive-tx'
// Alice's memo key
const alicePrivate = PrivateKey.fromLogin('alice', 'password', 'memo')
// Bob's memo key (public)
const bobPrivate = PrivateKey.fromLogin('bob', 'password', 'memo')
const bobPublic = bobPrivate.createPublic()
// Encrypt message from Alice to Bob
const message = '#Hello Bob, this is a secret message!'
const encrypted = Memo.encode(alicePrivate, bobPublic, message)
console.log('Encrypted:', encrypted)
// Output: #base58_encoded_encrypted_data...
// Plain text message (no encryption)
const plainText = Memo.encode(alicePrivate, bobPublic, 'Public message')
console.log('Plain text:', plainText)
// Output: Public message
decode
Decrypts an encrypted memo using AES decryption.
const decryptedMemo = Memo.decode(privateKey, memo)
privateKey
string | PrivateKey
required
Recipient’s private memo key (WIF string or PrivateKey instance)
Encrypted memo string. Must start with # for decryption. Plain text (without #) is returned unchanged.
Decrypted memo content with # prefix, or original string if not encrypted
Example
import { Memo, PrivateKey } from 'hive-tx'
// Bob receives an encrypted memo
const bobPrivate = PrivateKey.fromLogin('bob', 'password', 'memo')
// Encrypted memo received in a transfer
const encryptedMemo = '#4LmkPLQKBkfCHhfJ...' // From transaction
// Decrypt the memo
const decrypted = Memo.decode(bobPrivate, encryptedMemo)
console.log('Decrypted:', decrypted)
// Output: #Hello Bob, this is a secret message!
// Plain text memo (no decryption needed)
const plainText = Memo.decode(bobPrivate, 'Public message')
console.log('Plain text:', plainText)
// Output: Public message
Complete Example: Encrypted Transfer
import { Transaction, PrivateKey, Memo } from 'hive-tx'
async function sendEncryptedTransfer() {
// Alice's keys
const aliceActiveKey = PrivateKey.fromLogin('alice', 'password', 'active')
const aliceMemoKey = PrivateKey.fromLogin('alice', 'password', 'memo')
// Bob's public memo key (get from blockchain or derive)
const bobMemoKey = PrivateKey.fromLogin('bob', 'bobpassword', 'memo')
const bobPublicMemo = bobMemoKey.createPublic()
// Encrypt the memo
const message = '#Payment for web development services - Invoice #12345'
const encryptedMemo = Memo.encode(aliceMemoKey, bobPublicMemo, message)
// Create transfer with encrypted memo
const tx = new Transaction()
await tx.addOperation('transfer', {
from: 'alice',
to: 'bob',
amount: '50.000 HIVE',
memo: encryptedMemo
})
// Sign with active key
tx.sign(aliceActiveKey)
// Broadcast
const result = await tx.broadcast()
console.log('Transfer sent:', result.tx_id)
console.log('Encrypted memo:', encryptedMemo)
}
sendEncryptedTransfer()
Decrypting Received Memos
import { Memo, PrivateKey } from 'hive-tx'
import { Client } from '@hiveio/dhive'
const client = new Client(['https://api.hive.blog'])
async function readEncryptedMemos(username: string, memoKey: PrivateKey) {
// Get recent account history
const history = await client.database.getAccountHistory(username, -1, 100)
// Filter for transfers to this account
const transfers = history.filter(([, op]) => {
return op[0] === 'transfer' && op[1].to === username
})
// Decrypt memos
for (const [, op] of transfers) {
const transfer = op[1]
if (transfer.memo.startsWith('#')) {
try {
const decrypted = Memo.decode(memoKey, transfer.memo)
console.log('From:', transfer.from)
console.log('Amount:', transfer.amount)
console.log('Memo:', decrypted)
} catch (error) {
console.error('Failed to decrypt memo:', error.message)
}
} else {
console.log('Plain text memo:', transfer.memo)
}
}
}
// Usage
const bobMemoKey = PrivateKey.fromLogin('bob', 'password', 'memo')
readEncryptedMemos('bob', bobMemoKey)
Bidirectional Encryption
Memos can be decrypted by both sender and recipient:
import { Memo, PrivateKey } from 'hive-tx'
// Alice's keys
const aliceMemoKey = PrivateKey.fromLogin('alice', 'password', 'memo')
const alicePublicMemo = aliceMemoKey.createPublic()
// Bob's keys
const bobMemoKey = PrivateKey.fromLogin('bob', 'password', 'memo')
const bobPublicMemo = bobMemoKey.createPublic()
// Alice encrypts message to Bob
const message = '#Secret meeting at noon'
const encrypted = Memo.encode(aliceMemoKey, bobPublicMemo, message)
console.log('Encrypted:', encrypted)
// Bob decrypts (as recipient)
const bobDecrypted = Memo.decode(bobMemoKey, encrypted)
console.log('Bob reads:', bobDecrypted)
// Output: #Secret meeting at noon
// Alice can also decrypt her own sent message
const aliceDecrypted = Memo.decode(aliceMemoKey, encrypted)
console.log('Alice reads:', aliceDecrypted)
// Output: #Secret meeting at noon
Unicode Support
Memos fully support Unicode characters:
import { Memo, PrivateKey } from 'hive-tx'
const aliceMemoKey = PrivateKey.fromLogin('alice', 'password', 'memo')
const bobMemoKey = PrivateKey.fromLogin('bob', 'password', 'memo')
const bobPublicMemo = bobMemoKey.createPublic()
// Messages with Unicode
const messages = [
'#Hello 世界 🌍',
'#Café ☕',
'#Привет мир',
'#مرحبا بالعالم',
'#🚀 To the moon! 🌙'
]
for (const message of messages) {
const encrypted = Memo.encode(aliceMemoKey, bobPublicMemo, message)
const decrypted = Memo.decode(bobMemoKey, encrypted)
console.log('Original:', message)
console.log('Encrypted:', encrypted)
console.log('Decrypted:', decrypted)
console.log('Match:', message === decrypted)
console.log('---')
}
Fetching Public Memo Keys
To encrypt a memo, you need the recipient’s public memo key:
import { Memo, PrivateKey, PublicKey } from 'hive-tx'
import { Client } from '@hiveio/dhive'
const client = new Client(['https://api.hive.blog'])
async function encryptMemoForUser(
senderMemoKey: PrivateKey,
recipientUsername: string,
message: string
): Promise<string> {
// Fetch recipient's account data
const [account] = await client.database.getAccounts([recipientUsername])
if (!account) {
throw new Error(`Account ${recipientUsername} not found`)
}
// Get memo key from account
const recipientPublicMemo = PublicKey.fromString(account.memo_key)
// Encrypt message
const encrypted = Memo.encode(senderMemoKey, recipientPublicMemo, message)
return encrypted
}
// Usage
const aliceMemoKey = PrivateKey.fromLogin('alice', 'password', 'memo')
const encrypted = await encryptMemoForUser(
aliceMemoKey,
'bob',
'#Private message for Bob'
)
console.log('Encrypted memo:', encrypted)
Error Handling
import { Memo, PrivateKey } from 'hive-tx'
const memoKey = PrivateKey.fromLogin('alice', 'password', 'memo')
const bobPublicKey = PrivateKey.fromLogin('bob', 'password', 'memo').createPublic()
try {
// Encryption error (environment check)
const encrypted = Memo.encode(memoKey, bobPublicKey, '#Test')
console.log('Encrypted successfully')
} catch (error) {
console.error('Encryption failed:', error.message)
// Might output: This environment does not support encryption.
}
try {
// Decryption error (wrong key or corrupted data)
const corrupted = '#InvalidBase58Data'
const decrypted = Memo.decode(memoKey, corrupted)
} catch (error) {
console.error('Decryption failed:', error.message)
}
Plain Text vs Encrypted
Memos without the # prefix are treated as plain text:
import { Memo, PrivateKey } from 'hive-tx'
const memoKey = PrivateKey.fromLogin('alice', 'password', 'memo')
const bobPublicKey = PrivateKey.fromLogin('bob', 'password', 'memo').createPublic()
// Encrypted (starts with #)
const encrypted = Memo.encode(memoKey, bobPublicKey, '#Private message')
console.log('Encrypted:', encrypted.startsWith('#')) // true
console.log('Length:', encrypted.length) // Long base58 string
// Plain text (no #)
const plainText = Memo.encode(memoKey, bobPublicKey, 'Public message')
console.log('Plain text:', plainText) // 'Public message'
console.log('Unchanged:', plainText === 'Public message') // true
// Decoding plain text returns as-is
const decoded = Memo.decode(memoKey, 'Public message')
console.log('Decoded:', decoded) // 'Public message'
Security Considerations
Memo Security Best Practices:
- Use memo keys only: Never use active or owner keys for memo encryption
- Memo key compromise: If your memo key is compromised, past encrypted memos can be decrypted
- Public visibility: Encrypted memos are visible on the blockchain, only the content is encrypted
- Key management: Store memo private keys securely, separate from other keys
- Sensitive data: Avoid putting highly sensitive data in memos; they’re not as secure as off-chain encryption
Secure Memo Example
import { Memo, PrivateKey } from 'hive-tx'
// ✅ GOOD: Use memo key for encryption
const memoKey = PrivateKey.fromLogin('alice', 'password', 'memo')
const bobMemoPublic = PrivateKey.fromLogin('bob', 'password', 'memo').createPublic()
const encrypted = Memo.encode(memoKey, bobMemoPublic, '#Secret message')
// ❌ BAD: Using active key for memos
const activeKey = PrivateKey.fromLogin('alice', 'password', 'active')
// Don't do this - use dedicated memo keys
How It Works
Memo encryption uses the following process:
- Shared Secret: ECDH key exchange between sender’s private memo key and recipient’s public memo key creates a shared secret
- AES Encryption: The message is encrypted with AES using the shared secret as the key
- Nonce: A random nonce ensures different ciphertexts for identical messages
- Checksum: A checksum is included to detect corruption
- Base58 Encoding: The encrypted data is encoded in Base58 for transmission
- Format:
#[base58_encoded(from_key + to_key + nonce + checksum + encrypted_message)]
Type Definition
type Memo = {
encode(
privateKey: string | PrivateKey,
publicKey: string | PublicKey,
memo: string,
testNonce?: any
): string
decode(privateKey: string | PrivateKey, memo: string): string
}
See Also