Skip to content

Commit

Permalink
Merge pull request #36 from mistdale/bip39_passphrase_and_child
Browse files Browse the repository at this point in the history
BIP39 passphrase and child wallets
  • Loading branch information
sammchardy committed Apr 29, 2021
2 parents b2bfb34 + 9632ecc commit acd0800
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 12 deletions.
7 changes: 6 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,8 @@ You may also use the `Ledger Wallet class <#ledger>`_ to utilise your Ledger Har

It can be initialised with your private key or your mnemonic phrase.

You can additionally provide BIP39 passphrase and derived wallet id.

Note that the BinanceEnvironment used for the wallet must match that of the HttpApiClient, testnet addresses will not
work on the production system.

Expand Down Expand Up @@ -254,7 +256,10 @@ see examples below
from binance_chain.environment import BinanceEnvironment
testnet_env = BinanceEnvironment.get_testnet_env()
wallet = Wallet.create_wallet_from_mnemonic('mnemonic word string', env=testnet_env)
wallet = Wallet.create_wallet_from_mnemonic('mnemonic word string',
passphrase='optional passphrase',
child=0,
env=testnet_env)
print(wallet.address)
print(wallet.private_key)
print(wallet.public_key_hex)
Expand Down
50 changes: 39 additions & 11 deletions binance_chain/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class MnemonicLanguage(str, Enum):

class BaseWallet:

HD_PATH = "44'/714'/0'/0/0"
HD_PATH = "44'/714'/0'/0/{id}"

def __init__(self, env: Optional[BinanceEnvironment] = None):
self._env = env or BinanceEnvironment.get_production_env()
Expand Down Expand Up @@ -103,8 +103,21 @@ def sign_message(self, msg_bytes):


class Wallet(BaseWallet):
"""
Usage example:
HD_PATH = "44'/714'/0'/0/0"
m = Wallet.create_random_mnemonic() # 12 words
p = 'my secret passphrase' # bip39 passphrase
# Store <m> and <p> somewhere safe
wallet1 = Wallet.create_wallet_from_mnemonic(m, passphrase=p, child=0, env=testnet_env)
wallet2 = Wallet.create_wallet_from_mnemonic(m, passphrase=p, child=1, env=testnet_env)
...
"""

HD_PATH = "44'/714'/0'/0/{id}"

def __init__(self, private_key, env: Optional[BinanceEnvironment] = None):
super().__init__(env)
Expand All @@ -118,25 +131,40 @@ def create_random_wallet(cls, language: MnemonicLanguage = MnemonicLanguage.ENGL
env: Optional[BinanceEnvironment] = None):
"""Create wallet with random mnemonic code
:return:
:return: initialised Wallet
"""
m = Mnemonic(language.value)
phrase = m.generate()
phrase = cls.create_random_mnemonic(language)
return cls.create_wallet_from_mnemonic(phrase, env=env)

@classmethod
def create_wallet_from_mnemonic(cls, mnemonic: str, env: Optional[BinanceEnvironment] = None):
"""Create wallet with random mnemonic code
def create_wallet_from_mnemonic(cls, mnemonic: str,
passphrase: Optional[str] = '',
child: Optional[int] = 0,
env: Optional[BinanceEnvironment] = None):
"""Create wallet from mnemonic, passphrase and child wallet id
:return:
:return: initialised Wallet
"""
seed = Mnemonic.to_seed(mnemonic)
parent_wallet = network.keys.bip32_seed(seed)
child_wallet = parent_wallet.subkey_for_path(Wallet.HD_PATH)
if type(child) != int:
raise TypeError("Child wallet id should be of type int")

seed = Mnemonic.to_seed(mnemonic, passphrase)
new_wallet = network.keys.bip32_seed(seed)
child_wallet = new_wallet.subkey_for_path(Wallet.HD_PATH)
# convert secret exponent (private key) int to hex
key_hex = format(child_wallet.secret_exponent(), 'x')
return cls(key_hex, env=env)

@classmethod
def create_random_mnemonic(cls, language: MnemonicLanguage = MnemonicLanguage.ENGLISH):
"""Create random mnemonic code
:return: str, mnemonic phrase
"""
m = Mnemonic(language.value)
phrase = m.generate()
return phrase

@property
def private_key(self):
return self._private_key
Expand Down

0 comments on commit acd0800

Please sign in to comment.