wallet_manager
Ethereum Wallet Manager Module
Handles HD wallet creation, derivation, and encrypted storage in Redis. Supports any EVM-compatible network through configurable RPC endpoints. Uses BIP39 mnemonics and BIP44 derivation paths (m/44’/60’/0’/0/x).
- class wallet_manager.WalletManager[source]
Bases:
objectManages Ethereum HD wallets with encrypted storage in Redis.
Features: - BIP39 mnemonic generation and import - BIP44 derivation (m/44’/60’/0’/0/x) - AES-256-GCM encryption for private keys at rest - Per-user wallet isolation - Address caching for derived addresses
Unlike the v2 singleton, this class accepts an async Redis client via its async methods so it can work with the v3 ToolContext.redis.
- __init__()[source]
Create a wallet manager with no master key loaded yet.
Leaves
_master_keyasNone; it is fetched lazily by_ensure_master_key()on the first operation that needs to encrypt or decrypt, so construction touches no Redis or environment. The module instantiates a process-wide singletonwallet_managerat import time (imported bytools/eth_wallet_toolsand the test suite), so this constructor is effectively called once per process.
- static generate_mnemonic(strength=128)[source]
Generate a fresh BIP39 English mnemonic seed phrase.
Uses the
mnemoniclibrary (imported lazily) to produce a new random recovery phrase whose word count follows the entropy strength (128 bits -> 12 words, 256 -> 24). Pure CPU helper with no I/O. Called bycreate_wallet()when no mnemonic is supplied, and directly by the wallet tool layers (tools/eth_wallet_tools,tools/btc_wallet_tools) when generating a new wallet for a user.
- static validate_mnemonic(mnemonic)[source]
Check that a string is a valid BIP39 English mnemonic.
Verifies both the wordlist membership and the embedded checksum via the
mnemoniclibrary (imported lazily). Pure CPU helper with no I/O. Called bycreate_wallet()to reject a bad imported phrase, and by the tool layers (tools/eth_wallet_tools,tools/btc_wallet_tools) and the identity-registry tests to validate user- or seed-supplied phrases.
- static derive_address_from_mnemonic(mnemonic, index=0)[source]
Derive the EVM address and private key at one BIP44 index.
Walks the BIP44 Ethereum derivation path
m/44'/60'/0'/0/{index}from the given mnemonic usingeth_account(enabling its unaudited HD wallet feature first), returning the checksummed address and its hex private key. Pure CPU helper with no I/O. Called bycreate_wallet()(index 0),derive_address()(caching newly derived addresses), andget_private_key()(to recover the key at a given index).
- static derive_address_from_private_key(private_key)[source]
Compute the EVM address for a raw private key.
Normalises the key to
0x-prefixed form and loads it througheth_account.Account.from_keyto obtain the checksummed address. Pure CPU helper with no I/O. Called byimport_private_key()when registering a simple (non-HD) wallet.
- static is_valid_private_key(private_key)[source]
Report whether a string is a usable EVM private key.
Attempts to load the (
0x-normalised) key viaeth_account.Account.from_keyand returnsFalseon any exception, making it a safe pre-flight check that never raises. Pure CPU helper with no I/O. Called byimport_private_key()to reject malformed input before storing anything.
- async create_wallet(user_id, wallet_name, redis_client, mnemonic=None, strength=128)[source]
Create and persist a new HD wallet for a user.
Validates and normalises wallet_name (1-32 alphanumeric chars plus
-/_), ensures it does not already exist across the user’s aliases, then either validates the supplied mnemonic or generates a fresh one viagenerate_mnemonic(). It derives the index-0 address withderive_address_from_mnemonic(), encrypts the mnemonic under a per-wallet key (_derive_wallet_key()then_encrypt()), and writes the wallet record to theeth_wallet:{user_id}:{wallet_name}Redis key while adding the name to theeth_wallet_index:{user_id}set. Touches the master key (_ensure_master_key()) and Redis (SETplusSADD); logs the creation. Invoked bytools/eth_wallet_tools(thecreate/importflows) and the identity-registry tests.- Parameters:
user_id (
str) – The user the wallet belongs to.wallet_name (
str) – Desired wallet name (case-folded, validated).redis_client – The async Redis client for persistence.
mnemonic (
Optional[str]) – An existing BIP39 phrase to import, orNoneto generate one.strength (
int) – Entropy in bits when generating a new mnemonic.
- Returns:
wallet_name,type("hd"),address, andcreated_atfor the new wallet (the seed is never returned).- Return type:
- Raises:
ValueError – If the name is invalid, the wallet already exists, or a supplied mnemonic fails validation.
- async import_private_key(user_id, wallet_name, private_key, redis_client)[source]
Import a raw private key as a simple (non-HD) wallet.
Mirrors
create_wallet()but for a single externally-supplied key: it validates the wallet name, ensures the wallet does not already exist, normalises and validates the key (is_valid_private_key()), derives its address (derive_address_from_private_key()), encrypts the key under a per-wallet key (_derive_wallet_key()then_encrypt()), and stores atype: simplerecord ateth_wallet:{user_id}:{wallet_name}while adding the name to theeth_wallet_index:{user_id}set. Touches the master key (_ensure_master_key()) and Redis (SETplusSADD); logs the import. Called bytools/eth_wallet_tools.- Parameters:
- Returns:
wallet_name,type("simple"),address, andcreated_at(the key itself is never returned).- Return type:
- Raises:
ValueError – If the name is invalid, the wallet already exists, or the private key is invalid.
- async wallet_exists(user_id, wallet_name, redis_client, config=None)[source]
Report whether a named wallet exists for the user (any alias).
Expands the user into all linked identities via
_get_user_aliases()and checks each alias’seth_wallet:{alias}:{wallet_name}Redis key (EXISTS), returningTrueon the first hit so a wallet created on one platform is visible across them all. Reads Redis only. Called bycreate_wallet()andimport_private_key()as a uniqueness guard, and directly by the identity-registry tests.- Parameters:
- Returns:
Trueif the wallet exists under any alias, elseFalse.- Return type:
- async get_wallet(user_id, wallet_name, redis_client, config=None)[source]
Fetch a wallet’s public metadata (never its seed) for the user.
Resolves the user’s aliases via
_get_user_aliases()and reads eacheth_wallet:{alias}:{wallet_name}Redis key (GET) until one hits, then returns the non-secret fields — name, type, derived addresses, and creation time — deliberately omitting theencrypted_seed. Reads Redis only. Called bylist_wallets()andget_private_key()internally, and directly by the wallet tool layers (tools/eth_wallet_tools,tools/btc_wallet_tools) and tests.- Parameters:
- Returns:
wallet_name,type,addresses, andcreated_at, orNoneif no matching wallet exists.- Return type:
- async get_decrypted_seed(user_id, wallet_name, redis_client, config=None)[source]
Decrypt and return a wallet’s seed (mnemonic or private key).
Loads the master key (
_ensure_master_key()), then for each of the user’s aliases (_get_user_aliases()) reads theeth_wallet:{alias}:{wallet_name}record from Redis, re-derives that wallet’s key with_derive_wallet_key(), and decrypts the storedencrypted_seedvia_decrypt(). Because the per-wallet key is salted with the owning alias, it retries the next alias on anInvalidTagorValueError(logging the failure) so the right identity’s key eventually matches. Returns the raw secret, so callers must treat it carefully. Reads Redis and performs decryption. Called internally byderive_address()andget_private_key(), and directly bytools/eth_wallet_tools,tools/btc_wallet_tools, and the tests.- Parameters:
- Returns:
The decrypted mnemonic or private key, or
Noneif the wallet is missing, has no seed, or cannot be decrypted under any alias.- Return type:
- async derive_address(user_id, wallet_name, index, redis_client, config=None)[source]
Return the EVM address at a given index, deriving and caching it.
Loads the master key, resolves aliases, and finds the wallet record in Redis. If the requested index is already in the cached
addressesmap it returns it directly. For a simple (non-HD) wallet only index 0 exists, so any other index raises. For an HD wallet it fetches the decrypted seed (get_decrypted_seed()), derives the address withderive_address_from_mnemonic(), writes the newly derived address back into the wallet record (a RedisSET) so future lookups are free, and returns it. Reads and writes Redis and performs decryption. Called bytools/eth_wallet_toolsfor address display, sends, and balance checks, and by the identity-registry tests.- Parameters:
- Returns:
The address at index, or
Noneif the wallet or its seed cannot be found.- Return type:
- Raises:
ValueError – If a non-zero index is requested from a simple wallet.
- async get_private_key(user_id, wallet_name, redis_client, index=0, config=None)[source]
Return the private key for a wallet address at a given index.
Loads the master key and, for each alias, looks the wallet up via
get_wallet()and decrypts its seed viaget_decrypted_seed(). For a simple wallet the stored seed is the private key (only index 0 is valid); for an HD wallet it derives the key at index withderive_address_from_mnemonic(). Reads Redis and performs decryption, and returns secret key material, so callers (e.g. transaction signing intools/eth_wallet_tools) must handle it carefully. Also exercised by the identity-registry tests.- Parameters:
- Returns:
The hex private key, or
Noneif the wallet or its seed cannot be found.- Return type:
- Raises:
ValueError – If a non-zero index is requested from a simple wallet.
- async list_wallets(user_id, redis_client, config=None)[source]
List all of a user’s wallets (public metadata) across aliases.
Resolves the user’s aliases (
_get_user_aliases()) and unions the members of eacheth_wallet_index:{alias}Redis set (SMEMBERS, decoding anybytes) into a de-duplicated set of names, then loads each one’s public metadata viaget_wallet()and returns them sorted by creation time. Reads Redis only; never exposes seeds. Called bytools/eth_wallet_toolsandtools/btc_wallet_toolsto render a user’s wallet list, and by the tests.
- async delete_wallet(user_id, wallet_name, redis_client, config=None)[source]
Delete a named wallet for every alias of the user.
Resolves the user’s aliases (
_get_user_aliases()) and, for each, deletes theeth_wallet:{alias}:{wallet_name}key (DEL); when a key was actually removed it also drops the name from theeth_wallet_index:{alias}set (SREM) and logs the deletion. Reports success if any alias’s wallet was removed. Writes to Redis. Called bytools/eth_wallet_toolsandtools/btc_wallet_toolsfor the user-facing delete flow, and by the tests.- Parameters:
- Returns:
Trueif a wallet was deleted under any alias, elseFalse.- Return type: