wallet: follow-up prev: always set wallet.gap_limit_for_change
What changed, and why it matters
This commit fixes a crash or incorrect behavior in Electrum wallets when creating change addresses. Previously, the code assumed every wallet had a numeric 'gap_limit_for_change' setting, but imported wallets did not set one, leaving it as None. When the code tried to use that None value as a number, it could fail. The patch gives all wallets a default value and safely falls back to zero if it is still unset, ensuring imported wallets can create change outputs without crashing.
No immediate user action required beyond updating to a version containing this fix. Developers should ensure imported wallets are included in tests covering transaction creation with change outputs.
Security signals we found
Defensive fix for missing attribute that could cause runtime failure during transaction construction
Imported wallets previously lacked gap_limit_for_change, potentially breaking change output generation
Crash during change-address selection could affect transaction creation reliability
Evidence from the diff
The change adds a class-level default gap_limit_for_change = None on Abstract_Wallet, then changes _get_change_addresses to use self.gap_limit_for_change or 0 instead of directly using the attribute. It also adds a type annotation gap_limit_for_change: int on Deterministic_Wallet. This prevents a TypeError or unexpected slicing behavior when Imported_Wallet (which does not define this attribute) reaches the change-address selection code path. The fix is defensive and follows up an earlier commit that introduced the attribute only for deterministic wallets.
Changed components
electrum/wallet.pyAbstract_WalletImported_WalletDeterministic_Wallettransaction change address selectionInspect captured patch +4 / −1
diff --git a/electrum/wallet.py b/electrum/wallet.py
index 4102fd7..cf68667 100644
--- a/electrum/wallet.py
+++ b/electrum/wallet.py
@@ -391,6 +391,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
"""
max_change_outputs = 3
+ gap_limit_for_change = None # type: int | None
txin_type: str
wallet_type: str
@@ -1882,7 +1883,8 @@ class Abstract_Wallet(ABC, Logger, EventListener):
# if there are none, take one randomly from the last few
if not allow_reuse:
return []
- addrs = self.get_change_addresses(slice_start=-self.gap_limit_for_change)
+ gap_limit = self.gap_limit_for_change or 0
+ addrs = self.get_change_addresses(slice_start=-gap_limit)
change_addrs = [random.choice(addrs)] if addrs else []
for addr in change_addrs:
assert is_address(addr), f"not valid bitcoin address: {addr}"
@@ -3808,6 +3810,7 @@ class Imported_Wallet(Simple_Wallet):
class Deterministic_Wallet(Abstract_Wallet):
+ gap_limit_for_change: int
def __init__(self, db, *, config):
self._ephemeral_addr_to_addr_index = {} # type: Dict[str, Sequence[int]]
Why this scored 33/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.