1
0

wallet: imports, whitespace, typing hints

This commit is contained in:
Sander van Grieken
2025-04-24 10:09:48 +02:00
parent 41c0558595
commit ae906fb17c

View File

@@ -44,45 +44,40 @@ import asyncio
import electrum_ecc as ecc import electrum_ecc as ecc
from aiorpcx import ignore_after, run_in_thread from aiorpcx import ignore_after, run_in_thread
from . import util, keystore, transaction, bitcoin, coinchooser, bip32, descriptor
from .i18n import _ from .i18n import _
from .bip32 import BIP32Node, convert_bip32_intpath_to_strpath, convert_bip32_strpath_to_intpath from .bip32 import BIP32Node, convert_bip32_intpath_to_strpath, convert_bip32_strpath_to_intpath
from . import util from .logging import get_logger, Logger
from .lntransport import extract_nodeid
from .util import ( from .util import (
NotEnoughFunds, UserCancelled, profiler, OldTaskGroup, format_fee_satoshis, NotEnoughFunds, UserCancelled, profiler, OldTaskGroup, format_fee_satoshis,
WalletFileException, BitcoinException, InvalidPassword, format_time, timestamp_to_datetime, WalletFileException, BitcoinException, InvalidPassword, format_time, timestamp_to_datetime,
Satoshis, Fiat, TxMinedInfo, quantize_feerate, OrderedDictWithIndex Satoshis, Fiat, TxMinedInfo, quantize_feerate, OrderedDictWithIndex, multisig_type, parse_max_spend,
OnchainHistoryItem, read_json_file, write_json_file, UserFacingException, FileImportFailed, EventListener,
event_listener
)
from .bitcoin import COIN, is_address, is_minikey, relayfee, dust_threshold, DummyAddress, DummyAddressUsedInTxException
from .keystore import (
load_keystore, Hardware_KeyStore, KeyStore, KeyStoreWithMPK, AddressIndexGeneric, CannotDerivePubkey
) )
from .simple_config import SimpleConfig from .simple_config import SimpleConfig
from .fee_policy import FeePolicy, FixedFeePolicy, FEE_RATIO_HIGH_WARNING, FEERATE_WARNING_HIGH_FEE from .fee_policy import FeePolicy, FixedFeePolicy, FEE_RATIO_HIGH_WARNING, FEERATE_WARNING_HIGH_FEE
from .lnutil import MIN_FUNDING_SAT
from .bitcoin import COIN, is_address, is_minikey, relayfee, dust_threshold
from .bitcoin import DummyAddress, DummyAddressUsedInTxException
from . import keystore
from .keystore import (load_keystore, Hardware_KeyStore, KeyStore, KeyStoreWithMPK,
AddressIndexGeneric, CannotDerivePubkey)
from .util import multisig_type, parse_max_spend
from .storage import StorageEncryptionVersion, WalletStorage from .storage import StorageEncryptionVersion, WalletStorage
from .wallet_db import WalletDB from .wallet_db import WalletDB
from . import transaction, bitcoin, coinchooser, bip32
from .transaction import ( from .transaction import (
Transaction, TxInput, TxOutput, PartialTransaction, PartialTxInput, Transaction, TxInput, TxOutput, PartialTransaction, PartialTxInput, PartialTxOutput, TxOutpoint, Sighash
PartialTxOutput, TxOutpoint, Sighash
) )
from .plugin import run_hook from .plugin import run_hook
from .address_synchronizer import (AddressSynchronizer, TX_HEIGHT_LOCAL, from .address_synchronizer import (
TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_FUTURE, TX_TIMESTAMP_INF) AddressSynchronizer, TX_HEIGHT_LOCAL, TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_FUTURE,
from .invoices import BaseInvoice, Invoice, Request TX_TIMESTAMP_INF
from .invoices import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED, PR_UNCONFIRMED, PR_INFLIGHT )
from .invoices import BaseInvoice, Invoice, Request, PR_PAID, PR_UNPAID, PR_EXPIRED, PR_UNCONFIRMED
from .contacts import Contacts from .contacts import Contacts
from .mnemonic import Mnemonic from .mnemonic import Mnemonic
from .logging import get_logger, Logger
from .lnworker import LNWallet from .lnworker import LNWallet
from .util import read_json_file, write_json_file, UserFacingException, FileImportFailed from .lnutil import MIN_FUNDING_SAT
from .util import EventListener, event_listener from .lntransport import extract_nodeid
from . import descriptor
from .descriptor import Descriptor from .descriptor import Descriptor
from .util import OnchainHistoryItem
from .txbatcher import TxBatcher from .txbatcher import TxBatcher
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -177,17 +172,18 @@ async def sweep(
fee_policy: FeePolicy, fee_policy: FeePolicy,
imax=100, imax=100,
locktime=None, locktime=None,
tx_version=None) -> PartialTransaction: tx_version=None
) -> PartialTransaction:
inputs, keypairs = await sweep_preparations(privkeys, network, imax) inputs, keypairs = await sweep_preparations(privkeys, network, imax)
total = sum(txin.value_sats() for txin in inputs) total = sum(txin.value_sats() for txin in inputs)
outputs = [PartialTxOutput(scriptpubkey=bitcoin.address_to_script(to_address), value=total)] outputs = [PartialTxOutput(scriptpubkey=bitcoin.address_to_script(to_address), value=total)]
tx = PartialTransaction.from_io(inputs, outputs) tx = PartialTransaction.from_io(inputs, outputs)
fee = fee_policy.estimate_fee(tx.estimated_size(), network=network) fee = fee_policy.estimate_fee(tx.estimated_size(), network=network)
if total - fee < 0: if total - fee < 0:
raise Exception(_('Not enough funds on address.') + '\nTotal: %d satoshis\nFee: %d'%(total, fee)) raise Exception(_('Not enough funds on address.') + '\nTotal: %d satoshis\nFee: %d' % (total, fee))
if total - fee < dust_threshold(network): if total - fee < dust_threshold(network):
raise Exception(_('Not enough funds on address.') + '\nTotal: %d satoshis\nFee: %d\nDust Threshold: %d'%(total, fee, dust_threshold(network))) raise Exception(_('Not enough funds on address.') +
'\nTotal: %d satoshis\nFee: %d\nDust Threshold: %d' % (total, fee, dust_threshold(network)))
outputs = [PartialTxOutput(scriptpubkey=bitcoin.address_to_script(to_address), value=total - fee)] outputs = [PartialTxOutput(scriptpubkey=bitcoin.address_to_script(to_address), value=total - fee)]
if locktime is None: if locktime is None:
locktime = get_locktime_for_new_transaction(network) locktime = get_locktime_for_new_transaction(network)
@@ -228,6 +224,8 @@ def get_locktime_for_new_transaction(
class CannotRBFTx(Exception): pass class CannotRBFTx(Exception): pass
class TransactionPotentiallyDangerousException(Exception): pass
class TransactionDangerousException(TransactionPotentiallyDangerousException): pass
class CannotBumpFee(CannotRBFTx): class CannotBumpFee(CannotRBFTx):
@@ -251,12 +249,6 @@ class InternalAddressCorruption(Exception):
"Please restore your wallet from seed, and compare the addresses in both files") "Please restore your wallet from seed, and compare the addresses in both files")
class TransactionPotentiallyDangerousException(Exception): pass
class TransactionDangerousException(TransactionPotentiallyDangerousException): pass
class TxSighashRiskLevel(enum.IntEnum): class TxSighashRiskLevel(enum.IntEnum):
# higher value -> more risk # higher value -> more risk
SAFE = 0 SAFE = 0
@@ -359,6 +351,7 @@ class TxWalletDelta(NamedTuple):
delta: int delta: int
fee: Optional[int] fee: Optional[int]
class TxWalletDetails(NamedTuple): class TxWalletDetails(NamedTuple):
txid: Optional[str] txid: Optional[str]
status: str status: str
@@ -412,19 +405,19 @@ class Abstract_Wallet(ABC, Logger, EventListener):
self._last_full_history = None self._last_full_history = None
self._tx_parents_cache = {} self._tx_parents_cache = {}
self._default_labels = {} self._default_labels = {}
self._accounting_addresses = set() # addresses counted as ours after successful sweep self._accounting_addresses = set() # addresses counted as ours after successful sweep
self.taskgroup = OldTaskGroup() self.taskgroup = OldTaskGroup()
# saved fields # saved fields
self.use_change = db.get('use_change', True) self.use_change = db.get('use_change', True)
self.multiple_change = db.get('multiple_change', False) self.multiple_change = db.get('multiple_change', False)
self._labels = db.get_dict('labels') self._labels = db.get_dict('labels')
self._frozen_addresses = set(db.get('frozen_addresses', [])) self._frozen_addresses = set(db.get('frozen_addresses', []))
self._frozen_coins = db.get_dict('frozen_coins') # type: Dict[str, bool] self._frozen_coins = db.get_dict('frozen_coins') # type: Dict[str, bool]
self.fiat_value = db.get_dict('fiat_value') self.fiat_value = db.get_dict('fiat_value')
self._receive_requests = db.get_dict('payment_requests') # type: Dict[str, Request] self._receive_requests = db.get_dict('payment_requests') # type: Dict[str, Request]
self._invoices = db.get_dict('invoices') # type: Dict[str, Invoice] self._invoices = db.get_dict('invoices') # type: Dict[str, Invoice]
self._reserved_addresses = set(db.get('reserved_addresses', [])) self._reserved_addresses = set(db.get('reserved_addresses', []))
self._num_parents = db.get_dict('num_parents') self._num_parents = db.get_dict('num_parents')
@@ -977,7 +970,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
else: else:
assert isinstance(tx, PartialTransaction) assert isinstance(tx, PartialTransaction)
s, r = tx.signature_count() s, r = tx.signature_count()
status = _("Unsigned") if s == 0 else _('Partially signed') + ' (%d/%d)'%(s,r) status = _("Unsigned") if s == 0 else _('Partially signed') + ' (%d/%d)' % (s, r)
if is_relevant: if is_relevant:
if tx_wallet_delta.is_all_input_ismine: if tx_wallet_delta.is_all_input_ismine:
@@ -1276,7 +1269,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
def get_invoices(self) -> List[Invoice]: def get_invoices(self) -> List[Invoice]:
out = list(self._invoices.values()) out = list(self._invoices.values())
out.sort(key=lambda x:x.time) out.sort(key=lambda x: x.time)
return out return out
def get_unpaid_invoices(self) -> List[Invoice]: def get_unpaid_invoices(self) -> List[Invoice]:
@@ -1644,10 +1637,10 @@ class Abstract_Wallet(ABC, Logger, EventListener):
label = request.get_message() label = request.get_message()
return label return label
def set_default_label(self, key:str, value:str): def set_default_label(self, key: str, value: str):
self._default_labels[key] = value self._default_labels[key] = value
def get_label_for_outpoint(self, outpoint:str) -> str: def get_label_for_outpoint(self, outpoint: str) -> str:
return self._labels.get(outpoint) or self._get_default_label_for_outpoint(outpoint) return self._labels.get(outpoint) or self._get_default_label_for_outpoint(outpoint)
def _get_default_label_for_outpoint(self, outpoint: str) -> str: def _get_default_label_for_outpoint(self, outpoint: str) -> str:
@@ -1742,7 +1735,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
time_str = format_time(timestamp) if timestamp else _("unknown") time_str = format_time(timestamp) if timestamp else _("unknown")
status_str = TX_STATUS[status] if status < 4 else time_str status_str = TX_STATUS[status] if status < 4 else time_str
if extra: if extra:
status_str += ' [%s]'%(', '.join(extra)) status_str += ' [%s]' % (', '.join(extra))
return status, status_str return status, status_str
def relayfee(self): def relayfee(self):
@@ -2015,10 +2008,10 @@ class Abstract_Wallet(ABC, Logger, EventListener):
val = int((amount/i_max_sum) * weight) val = int((amount/i_max_sum) * weight)
outputs[i].value = val outputs[i].value = val
distr_amount += val distr_amount += val
(x,i) = i_max[-1] (x, i) = i_max[-1]
outputs[i].value += (amount - distr_amount) outputs[i].value += (amount - distr_amount)
tx_inputs = inputs + coins # these do not overlap, see above tx_inputs = inputs + coins # these do not overlap, see above
distribute_amount(0) distribute_amount(0)
tx = PartialTransaction.from_io(list(tx_inputs), list(outputs)) tx = PartialTransaction.from_io(list(tx_inputs), list(outputs))
fee = fee_estimator(tx.estimated_size()) fee = fee_estimator(tx.estimated_size())
@@ -2032,7 +2025,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
self.logger.info(f'Adding change output to meet utxo reserve requirements') self.logger.info(f'Adding change output to meet utxo reserve requirements')
change_addr = self.get_change_addresses_for_new_transaction(change_addr)[0] change_addr = self.get_change_addresses_for_new_transaction(change_addr)[0]
change = PartialTxOutput.from_address_and_value(change_addr, self.config.LN_UTXO_RESERVE) change = PartialTxOutput.from_address_and_value(change_addr, self.config.LN_UTXO_RESERVE)
change.is_utxo_reserve = True # for GUI change.is_utxo_reserve = True # for GUI
outputs.append(change) outputs.append(change)
to_distribute -= change.value to_distribute -= change.value
tx = PartialTransaction.from_io(list(tx_inputs), list(outputs)) tx = PartialTransaction.from_io(list(tx_inputs), list(outputs))
@@ -2455,7 +2448,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
else: else:
raise CannotCPFP(_("Could not find suitable output")) raise CannotCPFP(_("Could not find suitable output"))
coins = self.adb.get_addr_utxo(address) coins = self.adb.get_addr_utxo(address)
item = coins.get(TxOutpoint.from_str(txid+':%d'%i)) item = coins.get(TxOutpoint.from_str(txid + ':%d' % i))
if not item: if not item:
raise CannotCPFP(_("Could not find coins for output")) raise CannotCPFP(_("Could not find coins for output"))
inputs = [item] inputs = [item]
@@ -2663,7 +2656,13 @@ class Abstract_Wallet(ABC, Logger, EventListener):
txout.is_change = self.is_change(address) txout.is_change = self.is_change(address)
self._add_txinout_derivation_info(txout, address, only_der_suffix=only_der_suffix) self._add_txinout_derivation_info(txout, address, only_der_suffix=only_der_suffix)
def sign_transaction(self, tx: Transaction, password, *, ignore_warnings: bool = False) -> Optional[PartialTransaction]: def sign_transaction(
self,
tx: Transaction,
password,
*,
ignore_warnings: bool = False
) -> Optional[PartialTransaction]:
""" returns tx if successful else None """ """ returns tx if successful else None """
if self.is_watching_only(): if self.is_watching_only():
return return
@@ -2785,7 +2784,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
paid, conf = self.is_onchain_invoice_paid(invoice) paid, conf = self.is_onchain_invoice_paid(invoice)
if not paid: if not paid:
if isinstance(invoice, Invoice): if isinstance(invoice, Invoice):
if status:=invoice.get_broadcasting_status(): if status := invoice.get_broadcasting_status():
return status return status
status = PR_UNPAID status = PR_UNPAID
elif conf == 0: elif conf == 0:
@@ -2877,7 +2876,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
with self.lock, self.transaction_lock: with self.lock, self.transaction_lock:
for txo in tx.outputs(): for txo in tx.outputs():
addr = txo.address addr = txo.address
if request:=self.get_request_by_addr(addr): if request := self.get_request_by_addr(addr):
request_keys.add(request.get_id()) request_keys.add(request.get_id())
for invoice_key in self._invoices_from_scriptpubkey_map.get(txo.scriptpubkey, set()): for invoice_key in self._invoices_from_scriptpubkey_map.get(txo.scriptpubkey, set()):
invoice_keys.add(invoice_key) invoice_keys.add(invoice_key)
@@ -2957,7 +2956,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
def add_payment_request(self, req: Request, *, write_to_disk: bool = True): def add_payment_request(self, req: Request, *, write_to_disk: bool = True):
request_id = req.get_id() request_id = req.get_id()
self._receive_requests[request_id] = req self._receive_requests[request_id] = req
if addr:=req.get_address(): if addr := req.get_address():
self._requests_addr_to_key[addr].add(request_id) self._requests_addr_to_key[addr].add(request_id)
if write_to_disk: if write_to_disk:
self.save_db() self.save_db()
@@ -2969,7 +2968,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
if req is None: if req is None:
return return
self._receive_requests.pop(request_id, None) self._receive_requests.pop(request_id, None)
if addr:=req.get_address(): if addr := req.get_address():
self._requests_addr_to_key[addr].discard(request_id) self._requests_addr_to_key[addr].discard(request_id)
if req.is_lightning() and self.lnworker: if req.is_lightning() and self.lnworker:
self.lnworker.delete_payment_info(req.rhash) self.lnworker.delete_payment_info(req.rhash)
@@ -3341,7 +3340,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
except Exception: except Exception:
zeroconf_nodeid = None zeroconf_nodeid = None
can_get_zeroconf_channel = (self.lnworker and self.config.ACCEPT_ZEROCONF_CHANNELS can_get_zeroconf_channel = (self.lnworker and self.config.ACCEPT_ZEROCONF_CHANNELS
and zeroconf_nodeid in self.lnworker.peers) and zeroconf_nodeid in self.lnworker.peers)
status = self.get_invoice_status(req) status = self.get_invoice_status(req)
if status == PR_EXPIRED: if status == PR_EXPIRED:
@@ -3381,7 +3380,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
if amount_sat < MIN_FUNDING_SAT: if amount_sat < MIN_FUNDING_SAT:
ln_is_error = True ln_is_error = True
ln_help = (_('Cannot receive this payment. Request at least {} ' ln_help = (_('Cannot receive this payment. Request at least {} '
'to purchase a Lightning channel from your service provider.') 'to purchase a Lightning channel from your service provider.')
.format(self.config.format_amount_and_units(amount_sat=MIN_FUNDING_SAT))) .format(self.config.format_amount_and_units(amount_sat=MIN_FUNDING_SAT)))
else: else:
ln_zeroconf_suggestion = True ln_zeroconf_suggestion = True
@@ -3410,7 +3409,6 @@ class Abstract_Wallet(ABC, Logger, EventListener):
ln_zeroconf_suggestion=ln_zeroconf_suggestion ln_zeroconf_suggestion=ln_zeroconf_suggestion
) )
def synchronize(self) -> int: def synchronize(self) -> int:
"""Returns the number of new addresses we generated.""" """Returns the number of new addresses we generated."""
return 0 return 0
@@ -3602,7 +3600,7 @@ class Imported_Wallet(Simple_Wallet):
for tx_hash in transactions_to_remove: for tx_hash in transactions_to_remove:
self.adb._remove_transaction(tx_hash) self.adb._remove_transaction(tx_hash)
self.set_label(address, None) self.set_label(address, None)
if req:= self.get_request_by_addr(address): if req := self.get_request_by_addr(address):
self.delete_request(req.get_id()) self.delete_request(req.get_id())
self.set_frozen_state_of_addresses([address], False, write_to_disk=False) self.set_frozen_state_of_addresses([address], False, write_to_disk=False)
pubkey = self.get_public_key(address) pubkey = self.get_public_key(address)
@@ -3639,7 +3637,8 @@ class Imported_Wallet(Simple_Wallet):
return unused_addrs return unused_addrs
def is_mine(self, address) -> bool: def is_mine(self, address) -> bool:
if not address: return False if not address:
return False
return self.db.has_imported_address(address) return self.db.has_imported_address(address)
def get_address_index(self, address) -> Optional[str]: def get_address_index(self, address) -> Optional[str]:
@@ -3668,7 +3667,7 @@ class Imported_Wallet(Simple_Wallet):
continue continue
addr = bitcoin.pubkey_to_address(txin_type, pubkey) addr = bitcoin.pubkey_to_address(txin_type, pubkey)
good_addr.append(addr) good_addr.append(addr)
self.db.add_imported_address(addr, {'type':txin_type, 'pubkey':pubkey}) self.db.add_imported_address(addr, {'type': txin_type, 'pubkey': pubkey})
self.adb.add_address(addr) self.adb.add_address(addr)
self.save_keystore() self.save_keystore()
if write_to_disk: if write_to_disk:
@@ -3788,7 +3787,7 @@ class Deterministic_Wallet(Abstract_Wallet):
return self.keystore.get_seed_type() return self.keystore.get_seed_type()
def change_gap_limit(self, value): def change_gap_limit(self, value):
'''This method is not called in the code, it is kept for console use''' """This method is not called in the code, it is kept for console use"""
value = int(value) value = int(value)
if value >= self.min_acceptable_gap(): if value >= self.min_acceptable_gap():
self.gap_limit = value self.gap_limit = value
@@ -3924,7 +3923,8 @@ class Deterministic_Wallet(Abstract_Wallet):
if der_suffix is not None: if der_suffix is not None:
# note: we already know the pubkey belongs to the keystore, # note: we already know the pubkey belongs to the keystore,
# but the script template might be different # but the script template might be different
if len(der_suffix) != 2: continue if len(der_suffix) != 2:
continue
try: try:
my_address = self.derive_address(*der_suffix) my_address = self.derive_address(*der_suffix)
except CannotDerivePubkey: except CannotDerivePubkey:
@@ -4015,7 +4015,7 @@ class Multisig_Wallet(Deterministic_Wallet):
def load_keystore(self): def load_keystore(self):
self.keystores = {} self.keystores = {}
for i in range(self.n): for i in range(self.n):
name = 'x%d'%(i+1) name = 'x%d' % (i+1)
self.keystores[name] = load_keystore(self.db, name) self.keystores[name] = load_keystore(self.db, name)
self.keystore = self.keystores['x1'] self.keystore = self.keystores['x1']
xtype = bip32.xpub_type(self.keystore.xpub) xtype = bip32.xpub_type(self.keystore.xpub)
@@ -4069,9 +4069,11 @@ class Multisig_Wallet(Deterministic_Wallet):
wallet_types = ['standard', 'multisig', 'imported'] wallet_types = ['standard', 'multisig', 'imported']
def register_wallet_type(category): def register_wallet_type(category):
wallet_types.append(category) wallet_types.append(category)
wallet_constructors = { wallet_constructors = {
'standard': Standard_Wallet, 'standard': Standard_Wallet,
'old': Standard_Wallet, 'old': Standard_Wallet,
@@ -4079,16 +4081,18 @@ wallet_constructors = {
'imported': Imported_Wallet 'imported': Imported_Wallet
} }
def register_constructor(wallet_type, constructor): def register_constructor(wallet_type, constructor):
wallet_constructors[wallet_type] = constructor wallet_constructors[wallet_type] = constructor
# former WalletFactory # former WalletFactory
class Wallet(object): class Wallet(object):
"""The main wallet "entry point". """The main wallet "entry point".
This class is actually a factory that will return a wallet of the correct This class is actually a factory that will return a wallet of the correct
type when passed a WalletStorage instance.""" type when passed a WalletStorage instance."""
def __new__(self, db: 'WalletDB', *, config: SimpleConfig): def __new__(cls, db: 'WalletDB', *, config: SimpleConfig):
wallet_type = db.get('wallet_type') wallet_type = db.get('wallet_type')
WalletClass = Wallet.wallet_class(wallet_type) WalletClass = Wallet.wallet_class(wallet_type)
wallet = WalletClass(db, config=config) wallet = WalletClass(db, config=config)
@@ -4103,8 +4107,16 @@ class Wallet(object):
raise WalletFileException("Unknown wallet type: " + str(wallet_type)) raise WalletFileException("Unknown wallet type: " + str(wallet_type))
def create_new_wallet(*, path, config: SimpleConfig, passphrase=None, password=None, def create_new_wallet(
encrypt_file=True, seed_type=None, gap_limit=None) -> dict: *,
path,
config: SimpleConfig,
passphrase: Optional[str] = None,
password: Optional[str] = None,
encrypt_file: bool = True,
seed_type: Optional[str] = None,
gap_limit: Optional[int] = None
) -> dict:
"""Create a new wallet""" """Create a new wallet"""
storage = WalletStorage(path) storage = WalletStorage(path)
if storage.file_exists(): if storage.file_exists():