Skip to content

Commit db798c0

Browse files
authored
feat: add mnemonic setter (#2500)
1 parent 6554c24 commit db798c0

File tree

7 files changed

+106
-30
lines changed

7 files changed

+106
-30
lines changed

docs/userguides/accounts.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ test:
4646
number_of_accounts: 5
4747
```
4848
49+
You can also change settings at run-time, such as the mnemonic:
50+
51+
```python
52+
from ape import accounts
53+
54+
accounts.test_accounts.mnemonic = "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat"
55+
print(accounts.test_accounts[0])
56+
# 0x627306090abaB3A6e1400e9345bC60c78a8BEf57
57+
```
58+
4959
```{warning}
5060
NEVER put a seed phrase with real funds here.
5161
```

src/ape/api/accounts.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@
2929
from ape.types.signatures import MessageSignature, SignableMessage
3030
from ape.utils.basemodel import BaseInterfaceModel
3131
from ape.utils.misc import raises_not_implemented
32+
from ape.utils.testing import (
33+
DEFAULT_NUMBER_OF_TEST_ACCOUNTS,
34+
DEFAULT_TEST_HD_PATH,
35+
DEFAULT_TEST_MNEMONIC,
36+
)
3237

3338
if TYPE_CHECKING:
3439
from eth_pydantic_types import HexBytes
@@ -626,6 +631,30 @@ class TestAccountContainerAPI(AccountContainerAPI):
626631
``AccountContainerAPI`` directly. Then, they show up in the ``accounts`` test fixture.
627632
"""
628633

634+
@property
635+
def mnemonic(self) -> str:
636+
return self.config_manager.test.get("mnemonic", DEFAULT_TEST_MNEMONIC)
637+
638+
@mnemonic.setter
639+
def mnemonic(self, value: str):
640+
self.config_manager.test.mnemonic = value
641+
642+
@property
643+
def number_of_accounts(self) -> int:
644+
return self.config_manager.test.get("number_of_accounts", DEFAULT_NUMBER_OF_TEST_ACCOUNTS)
645+
646+
@number_of_accounts.setter
647+
def number_of_accounts(self, value: int):
648+
self.config_manager.test.number_of_accounts = value
649+
650+
@property
651+
def hd_path(self) -> str:
652+
return self.config_manager.test.get("hd_path", DEFAULT_TEST_HD_PATH)
653+
654+
@hd_path.setter
655+
def hd_path(self, value: str):
656+
self.config_manager.test.hd_path = value
657+
629658
@cached_property
630659
def data_folder(self) -> Path:
631660
"""

src/ape/managers/accounts.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from eth_utils import is_hex
88

9-
from ape.api.accounts import (
9+
from ape.api import (
1010
AccountAPI,
1111
AccountContainerAPI,
1212
ImpersonatedAccount,
@@ -53,6 +53,35 @@ def containers(self) -> dict[str, TestAccountContainerAPI]:
5353
for plugin_name, (container_type, account_type) in account_types
5454
}
5555

56+
@property
57+
def mnemonic(self) -> str:
58+
"""
59+
The seed phrase for generated test accounts.
60+
"""
61+
return self.config_manager.get_config("test").mnemonic
62+
63+
@mnemonic.setter
64+
def mnemonic(self, value: str):
65+
"""
66+
The seed phrase for generated test accounts.
67+
**WARNING**: Changing the test-mnemonic mid-session
68+
re-starts the provider (if connected to one).
69+
"""
70+
self.config_manager.test.mnemonic = value
71+
self.containers["test"].mnemonic = value
72+
73+
if provider := self.network_manager.active_provider:
74+
provider.update_settings({"mnemonic": value})
75+
76+
self._accounts_by_index = {}
77+
78+
@property
79+
def number_of_accounts(self) -> int:
80+
"""
81+
The number of test accounts to generate and fund by default.
82+
"""
83+
return self.config_manager.test.number_of_accounts
84+
5685
@property
5786
def hd_path(self) -> str:
5887
"""

src/ape/managers/project.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1952,7 +1952,7 @@ def reconfigure(self, **overrides):
19521952
Change a project's config.
19531953
19541954
Args:
1955-
**overrides: Config key-value pairs. Completely overridesfe
1955+
**overrides: Config key-value pairs. Completely overrides
19561956
existing.
19571957
"""
19581958

src/ape_accounts/accounts.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ def generate_account(
319319
Args:
320320
alias (str): The alias name of the account.
321321
passphrase (str): Passphrase used to encrypt the account storage file.
322-
hd_path (str): The hierarchal deterministic path to use when generating the account.
322+
hd_path (str): The hierarchical deterministic path to use when generating the account.
323323
Defaults to `m/44'/60'/0'/0/0`.
324324
word_count (int): The amount of words to use in the generated mnemonic.
325325
@@ -347,7 +347,7 @@ def import_account_from_mnemonic(
347347
alias (str): The alias name of the account.
348348
passphrase (str): Passphrase used to encrypt the account storage file.
349349
mnemonic (str): List of space-separated words representing the mnemonic seed phrase.
350-
hd_path (str): The hierarchal deterministic path to use when generating the account.
350+
hd_path (str): The hierarchical deterministic path to use when generating the account.
351351
Defaults to `m/44'/60'/0'/0/0`.
352352
353353
Returns:

src/ape_test/accounts.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,7 @@
1515
from ape.types.signatures import MessageSignature, TransactionSignature
1616
from ape.utils._web3_compat import sign_hash
1717
from ape.utils.misc import log_instead_of_fail
18-
from ape.utils.testing import (
19-
DEFAULT_NUMBER_OF_TEST_ACCOUNTS,
20-
DEFAULT_TEST_HD_PATH,
21-
DEFAULT_TEST_MNEMONIC,
22-
generate_dev_accounts,
23-
)
18+
from ape.utils.testing import generate_dev_accounts
2419

2520
if TYPE_CHECKING:
2621
from ape.api.transactions import TransactionAPI
@@ -36,21 +31,25 @@ def __init__(self, *args, **kwargs):
3631
def __len__(self) -> int:
3732
return self.number_of_accounts + len(self.generated_accounts)
3833

39-
@property
40-
def config(self):
41-
return self.config_manager.get_config("test")
42-
4334
@property
4435
def mnemonic(self) -> str:
45-
return self.config.get("mnemonic", DEFAULT_TEST_MNEMONIC)
36+
# Overridden so we can overload the setter.
37+
return self.config_manager.test.mnemonic
4638

47-
@property
48-
def number_of_accounts(self) -> int:
49-
return self.config.get("number_of_accounts", DEFAULT_NUMBER_OF_TEST_ACCOUNTS)
39+
@mnemonic.setter
40+
def mnemonic(self, mnemonic: str) -> None:
41+
# Overridden so we can also clear out generated accounts cache.
42+
self.config_manager.test.mnemonic = mnemonic
43+
self.generated_accounts = []
44+
45+
@TestAccountContainerAPI.mnemonic.setter
46+
def mnemonic(self, mnemonic: str) -> None:
47+
self.config_manager.test.mnemonic = mnemonic
48+
self.generated_accounts = []
5049

5150
@property
52-
def hd_path(self) -> str:
53-
return self.config.get("hd_path", DEFAULT_TEST_HD_PATH)
51+
def config(self):
52+
return self.config_manager.get_config("test")
5453

5554
@property
5655
def aliases(self) -> Iterator[str]:

tests/functional/test_accounts.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
)
2121
from ape.types.gas import AutoGasLimit
2222
from ape.types.signatures import recover_signer
23+
from ape.utils.testing import DEFAULT_TEST_MNEMONIC
2324
from ape_accounts.accounts import (
2425
KeyfileAccount,
2526
generate_account,
@@ -696,18 +697,26 @@ def test_using_different_hd_path(accounts, project, eth_tester_provider):
696697
assert old_address != new_address
697698

698699

699-
def test_using_random_mnemonic(accounts, project, eth_tester_provider):
700-
mnemonic = "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat"
701-
test_config = {"test": {"mnemonic": mnemonic}}
700+
def test_mnemonic(accounts):
701+
actual = accounts.mnemonic
702+
expected = DEFAULT_TEST_MNEMONIC
703+
assert actual == expected
702704

703-
old_address = accounts[0].address
704-
original_settings = eth_tester_provider.settings.model_dump(by_alias=True)
705-
with project.temp_config(**test_config):
706-
eth_tester_provider.update_settings(test_config["test"])
707-
new_address = accounts[0].address
708705

709-
eth_tester_provider.update_settings(original_settings)
710-
assert old_address != new_address
706+
def test_mnemonic_setter(accounts):
707+
original_mnemonic = accounts.mnemonic
708+
new_mnemonic = "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat"
709+
original_address = accounts[0].address
710+
711+
# Change.
712+
accounts.mnemonic = new_mnemonic
713+
new_address = accounts[0].address
714+
715+
# Put back.
716+
accounts.mnemonic = original_mnemonic
717+
718+
# Assert.
719+
assert new_address != original_address
711720

712721

713722
def test_iter_test_accounts(accounts):

0 commit comments

Comments
 (0)