Phase 1: new bal/i18n.py, BAL's own gettext layer (domain "bal", catalogs read with plugin.read_file()). _() asks Electrum's catalog first, then BAL's, then returns the English source. The Qt plugin loads the catalog of Electrum's GUI language at start-up; the CLI stays English. Phase 2: every user-visible GUI text is now a whole, extractable sentence (Ruff INT rules enabled). Class-level texts are marked with N_() and translated when shown. Stored data stays language-neutral: the status history is written in English and translated for display, the calendar defaults follow the GUI language, and the history label and wallet labels are never translated because BAL uses them to recognise its transactions. No visible change apart from the double colon fixed in the will detail. See CHANGELOG entries 58 and 59 and PLAN_I18N.md.
BAL — Bitcoin After Life (Electrum plugin)
Free and decentralized Bitcoin inheritance support for the Electrum wallet. Build time-locked "will" transactions that transfer your funds to your heirs if you stop refreshing them (dead-man's switch), optionally relayed by will-executor servers.
This repository contains a refactored and extended version of the original plugin. The logic was reorganized to cleanly separate business logic from the PyQt GUI, and new features have been added including a headless CLI, auto-rebuild on new transactions, OP_RETURN heirs, and configurable calendar reminders.
Repository layout
bal/ the installable Electrum plugin package
├── manifest.json plugin metadata (Electrum reads this)
├── qt.py Qt entry-point shim (re-exports Plugin)
├── cmdline.py CLI entry-point shim (re-exports Plugin)
├── core/ GUI-free logic (importable without Qt)
│ ├── util.py
│ ├── plugin_base.py
│ ├── heirs.py
│ ├── will.py
│ ├── willexecutors.py
│ ├── checkalive.py
│ ├── reminders.py
│ ├── qrtransfer.py BAL QR will-transfer wire format / chunk scheduler
│ ├── animated_qr.py BC-UR v1/v2 + BBQR codecs (stdlib-only)
│ └── input_rules.py
├── cli/ headless command-line layer (no Qt)
│ ├── commands.py bal_* daemon commands (@plugin_command)
│ ├── controller.py headless BalController (replicates BalWindow)
│ └── plugin.py CLI Plugin entry point
├── gui/qt/ PyQt6 presentation layer
│ ├── theme.py status → color mapping
│ ├── common.py shared imports / helpers
│ ├── widgets.py leaf widgets
│ ├── calendar.py calendar widget
│ ├── dialogs.py dialog windows
│ ├── lists.py tree/list views
│ ├── window.py per-wallet GUI controller
│ ├── window_utils.py GUI utility helpers
│ └── plugin.py Plugin (Electrum @hooks → GUI)
├── icons/ wallet_util/ LICENSE README.md
build_zip.py builds a clean, zipimport-friendly distribution zip
tests/ smoke + external-zip regression tests
Requirements
- Electrum 4.7.2 or 4.8.0 — the plugin detects which wallet-DB
registration API is available (
json_db.register_dicton 4.7.2,stored_dict.register_nameon 4.8.0) and adapts automatically. - PyQt6 (bundled with the Electrum desktop GUI).
Wallet compatibility
BAL currently supports standard (single-signature) wallets and
hardware wallets supported by Electrum. Multisig wallets and
Electrum TrustedCoin (2FA) wallets are not yet supported — see
COMPATIBILITY.md for the full compatibility matrix and
current status.
Installation
Build the distribution archive
python3 build_zip.py
# -> bal-electrum-plugin.zip (prints size + SHA-256 for integrity checks)
The builder writes a zipimport-friendly archive (files only, standard
DEFLATE, deterministic order) to avoid loader errors seen on some Electrum
portable builds.
Install as an external plugin (zip)
- Electrum → Tools → Plugins → install from file → pick the built zip.
- Enable Bitcoin After Life and restart Electrum.
- (Recommended) verify the downloaded zip's SHA-256 matches the value printed
by
build_zip.py.
Install as an internal plugin
Copy the bal/ directory into your Electrum installation's
electrum/plugins/ directory, so that electrum/plugins/bal/manifest.json
exists, then enable it from Tools → Plugins.
Transfer a will with QR codes (or audio)
From the will list (Export → QR Codes) a will can be exported as a
sequence of QR codes and imported on another device (Import via QR). The
export offers All / Valid / Valid-NC filters plus a QR size preset
(150–1800 bytes/frame) and ships the default BAL QR format already
compressed whenever that is smaller (best-of zlib, flag per frame); the import
flow reviews and sign each transaction
one at a time, then proposes exporting the signed transactions. When
Electrum's audio_modem plugin is enabled (optional, requires amodem +
PortAudio) Send/Receive audio buttons complement the QR channel. See
PLAN_QR_TRANSFER.md for the BAL QR wire-format spec.
Animated-QR formats (interop)
BAL QR is the default export format, but the export page's Format selector
also emits BC-UR v1 (ur:bytes, BC32 + SHA-256), BC-UR v2
(ur:bytes, CBOR fountain codes) and BBQR (B$…, Coinkite, used by
BitKit) animated-QR sequences. The importer auto-detects the format of each
code it sees, so any of the four formats can be imported on a BAL device, and
a BAL export can be imported by any tool that understands these standards.
UR v2 imports tolerate out-of-order and duplicate frames (fountain decoding);
BBQR frames may arrive in any order. Rotation/redundancy caps and the
32 MB message limit (zlib-bomb guard) bound untrusted scanner input.
Command-line / headless usage
BAL can be used without the Qt GUI via Electrum's daemon mode. The CLI layer
exposes bal_* commands that replicate the full inheritance cycle.
Prerequisites
- An Electrum daemon running (
electrum daemon -d) - A wallet loaded (
electrum load_wallet)
Available commands
| Category | Commands |
|---|---|
| Settings | bal_settings_list, bal_settings_get, bal_settings_set, bal_settings_reset |
| Heirs | bal_heirs_list, bal_heirs_show, bal_heirs_add, bal_heirs_update, bal_heirs_delete, bal_heirs_import, bal_heirs_export |
| Will-Executors | bal_willexecutors_list, bal_willexecutors_show, bal_willexecutors_add, bal_willexecutors_update, bal_willexecutors_select, bal_willexecutors_delete, bal_willexecutors_ping, bal_willexecutors_download, bal_willexecutors_import, bal_willexecutors_export |
| Will | bal_will_status, bal_will_check, bal_will_prepare, bal_will_autorebuild, bal_will_sign, bal_will_broadcast, bal_will_export, bal_will_import_merge, bal_will_invalidate, bal_will_check_executor |
Example workflow
electrum daemon -d
electrum load_wallet
electrum bal_heirs_list
electrum bal_will_prepare
electrum bal_will_sign --password '...'
electrum bal_will_broadcast
electrum stop
All commands require a running daemon (Electrum's plugin_command enforces
this). Wallet-bound commands (bal_heirs_*, bal_will_*, etc.) require the
wallet to be loaded first. Signing commands require --password for encrypted
wallets.
Inheritance safety: anticipate / postpone
A will transaction is signed with a fixed, immutable locktime and then optionally sent to will-executor servers, which are economically incentivised to broadcast it (they collect fees). Because the locktime is baked into the signed transaction, simply changing the delivery time later is not enough: the old, already-signed transaction keeps living on the will-executors.
The plugin handles the cases as follows (triggered when you press Prepare on the WILL tab):
- Anticipate (new delivery time earlier than the signed locktime, still in the future): a plain rebuild — the transactions are re-created with the new, earlier locktime. No on-chain invalidation and no Bitcoin fee, even if the will was already signed/sent: moving the date earlier only makes the inheritance available sooner, so there is no early-execution risk.
- Expire (new delivery time now in the past): the will is genuinely expired and you are asked to invalidate the old transaction on-chain, then rebuild.
- Postpone (new delivery time later than the signed locktime) on a will
that was already signed and/or pushed: the previously committed coins
must be invalidated on-chain first, otherwise a will-executor could
broadcast the old (earlier-locktime) transaction and execute the inheritance
too early. The plugin detects this by comparing the requested locktime with
the locktime frozen inside the signed transaction (
tx.locktime), and asks you to sign and broadcast an invalidation transaction. After it is broadcast, press Prepare again to rebuild, re-sign and re-send the new (postponed) inheritance. Postponing a will that was never signed/sent just rebuilds it (no on-chain fee).
Transaction list: the "Server" column
The will transaction list shows a dedicated Server column so you always know whether each inheritance transaction is actually stored on the will-executor servers, independently of the row colour:
| Label | Meaning |
|---|---|
Confirmed on server |
the will-executor confirmed it stored the transaction |
Sent (not checked) |
pushed to the will-executor, not yet re-checked |
Send failed / Not on server |
push failed or the server no longer has it |
Signed (not sent) |
signed locally, not sent to any will-executor |
Not sent |
not signed/sent yet |
Hovering the cell shows a tooltip with the will-executor URL and the current state.
Testing
Run the tests with the runtime environment active (see HANDOFF.md §3 for
the two venvs and how to activate them):
# imports + behavior
QT_QPA_PLATFORM=offscreen python3 tests/smoke_test.py electrum.plugins.bal
# external-zip loading regression (run after build_zip.py)
QT_QPA_PLATFORM=offscreen python3 tests/external_zip_test.py bal-electrum-plugin.zip
⚠️ Safety
This plugin builds real Bitcoin inheritance transactions with time-locks. Test on testnet or a fund-less wallet first, and review the generated transactions before broadcasting.
License
MIT — see bal/LICENSE.