Purpose: BCH key management, Cash Account registration/resolution, covenant creation, UTXO management
Complexity: Medium - Standard BCH wallet operations + Cash Account + covenant script building
The Wallet component handles all BCH operations:
name#number on-chain, resolve to BCH addressNo custody: User controls their own keys. No backend server holds funds.
What: Hierarchical Deterministic wallet - one seed phrase generates infinite addresses
Protocol:
m/44'/145'/0'/0/n (BCH = coin type 145)Pseudocode:
function generateWallet():
seed_phrase = generateBIP39Mnemonic(12 words)
master_key = deriveFromSeed(seed_phrase)
return {seed_phrase, master_key}
function deriveAddress(master_key, index):
path = "m/44'/145'/0'/0/" + index
private_key = derivePath(master_key, path)
public_key = getPublicKey(private_key)
address = pubKeyToAddress(public_key) // P2PKH format
return {address, private_key}
Security:
Error handling:
What: Register name#number on BCH blockchain via OP_RETURN
Protocol:
01010101 [name_hash] [address]Pseudocode:
function registerCashAccount(name, address):
// Check if name#1 exists
existing = queryCashAccount(name + "#1")
if existing:
// Find next available number
number = findNextNumber(name)
else:
number = 1
cash_account = name + "#" + number
// Build OP_RETURN transaction
op_return_data = buildCashAccountOP_RETURN(name, address)
tx = createTransaction({
outputs: [
{type: "OP_RETURN", data: op_return_data},
{type: "change", address: my_change_address}
]
})
broadcast(tx)
return cash_account
On-chain format:
OP_RETURN
0x01010101 // Cash Account protocol identifier
[name_bytes] // UTF-8 encoded name
[address_bytes] // BCH address (21 bytes)
Cost: ~€0.01 (single OP_RETURN transaction)
Error handling:
What: Lookup Elena#142 → BCH address
Protocol:
Pseudocode:
function resolveCashAccount(cash_account):
// Check local cache first
cached = getCachedAddress(cash_account)
if cached and not_expired(cached):
return cached.address
// Query blockchain
name, number = parseCashAccount(cash_account) // "Elena#142" → "Elena", 142
tx = electrumQuery("blockchain.transaction.get_cash_account", {
name: name,
number: number
})
if not tx:
return null // Cash Account not found
address = parseCashAccountOP_RETURN(tx.op_return_data)
// Cache for 24 hours
cacheAddress(cash_account, address, ttl=86400)
return address
Electrum query pattern:
{
"method": "blockchain.transaction.get_cash_account",
"params": {
"name": "Elena",
"number": 142
}
}
Response:
{
"tx_hash": "abc123...",
"op_return_data": "01010101456c656e61...",
"address": "bitcoincash:qp..."
}
Error handling:
Definition: BCH script that locks funds until specific conditions met
Asgaya use case:
Three parties:
Pseudocode:
function createCovenant(recipient_address, amount_satoshis, buffer_percent, expiry_hours):
buffer_satoshis = amount_satoshis * (buffer_percent / 100)
total_satoshis = amount_satoshis + buffer_satoshis
expiry_timestamp = now() + (expiry_hours * 3600)
covenant_script = buildScript({
recipient: recipient_address,
amount: amount_satoshis,
buffer: buffer_satoshis,
expiry: expiry_timestamp,
abort_threshold: 7 // Abort if BCH drops >7%
})
// Create transaction template (unfunded)
covenant_tx = {
version: 2,
inputs: [], // Seller will add when funding
outputs: [
{
value: total_satoshis,
script: covenant_script
}
],
locktime: 0
}
return {
covenant_id: hash(covenant_tx),
covenant_script: covenant_script,
amount: amount_satoshis,
buffer: buffer_satoshis,
expiry: expiry_timestamp
}
BCH Script (simplified):
OP_IF
// Recipient claim path
<recipient_pubkey> OP_CHECKSIG
OP_ELSE
// Expiry refund path
<expiry_timestamp> OP_CHECKLOCKTIMEVERIFY OP_DROP
<sender_pubkey> OP_CHECKSIG
OP_ENDIF
Actual implementation: More complex - includes price oracle checks, buffer return logic, abort conditions. See RS057 for full covenant specification.
States:
State transitions:
Created → Funded (Isabel detects payment, locks BCH)
Funded → Claimed (Elena claims, or Carlos co-signs)
Funded → Expired (8 hours, no claim)
Funded → Aborted (Price drop >7%)
Monitoring covenant state:
function getCovenantState(covenant_id):
// Query blockchain for covenant UTXO
utxo = electrumQuery("blockchain.utxo.get_info", {
covenant_id: covenant_id
})
if not utxo:
return "not_funded" // Template exists but no BCH locked
if utxo.spent:
// Check if claimed or refunded
spending_tx = electrumQuery("blockchain.transaction.get", utxo.spending_txid)
if isClaimTransaction(spending_tx):
return "claimed"
else:
return "expired_or_aborted"
// Check expiry
if now() > utxo.covenant_expiry:
return "expired"
// Check price abort
if priceDropped(utxo.funded_at_price, current_price) > 7:
return "aborted"
return "funded" // Active, waiting for claim
Problem: Wallet has multiple UTXOs (previous transactions). Which to spend for new transaction?
Strategy: Minimize fees while avoiding dust
Pseudocode:
function selectCoins(target_amount, fee_rate):
utxos = getWalletUTXOs()
// Sort by value (largest first)
utxos.sort(by: value, desc)
selected = []
total = 0
for utxo in utxos:
selected.push(utxo)
total += utxo.value
estimated_fee = calculateFee(selected.length, 2, fee_rate) // 2 outputs (payment + change)
if total >= target_amount + estimated_fee:
return {
inputs: selected,
change: total - target_amount - estimated_fee
}
return null // Insufficient funds
Alternative strategy (privacy): Random selection to avoid linking UTXOs
Problem: BCH fees are ~1 satoshi/byte. How much to pay?
Approach: Query Electrum for current fee rate
Pseudocode:
function estimateFee(num_inputs, num_outputs):
fee_rate = electrumQuery("blockchain.estimatefee") // satoshis per byte
// Estimate transaction size
tx_size = (num_inputs * 148) + (num_outputs * 34) + 10
fee = tx_size * fee_rate
return fee
Typical BCH transaction:
Error handling:
Problem: María sends €100, but her UTXO is €150. Where does €50 go?
Solution: Generate new change address (privacy)
Pseudocode:
function createTransaction(recipient, amount):
coins = selectCoins(amount, fee_rate)
if not coins:
return error("Insufficient funds")
change_address = deriveAddress(master_key, next_unused_index)
change_amount = coins.change
tx = {
inputs: coins.inputs,
outputs: [
{address: recipient, value: amount},
{address: change_address, value: change_amount} // Change back to María
]
}
return tx
Privacy note: Never reuse addresses. Each transaction uses new change address.
if total_balance < amount + fee:
show_error("Insufficient funds. You have €X, need €Y")
suggest_action("Receive BCH first or reduce amount")
try:
tx_id = broadcast(tx)
catch NetworkError:
queue_for_retry(tx)
show_message("Offline. Transaction queued.")
if tx_rejected_with("double-spend"):
// Another transaction already spent these UTXOs
refresh_wallet_state()
show_error("Transaction conflict. Wallet refreshed, try again.")
if covenant.expiry - now() < 1_hour:
show_warning("Covenant expires in " + time_remaining(covenant.expiry))
suggest_action("Recipient should claim soon or funds return to sender")
Uses:
Used by:
Status: Phase 0 - Implementation pending
Updated: 2026-06-25
Complexity: Medium (standard BCH operations + covenant building)
Research: See RS057 (BCH/Cash Accounts implementation details)
—
| 🏠 Home | ↑ Android App | 📖 Glossary |