Skip to content

Commit

Permalink
feat: add mnemonic setter (#2500)
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey authored Feb 11, 2025
1 parent 6554c24 commit db798c0
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 30 deletions.
10 changes: 10 additions & 0 deletions docs/userguides/accounts.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ test:
number_of_accounts: 5
```
You can also change settings at run-time, such as the mnemonic:
```python
from ape import accounts

accounts.test_accounts.mnemonic = "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat"
print(accounts.test_accounts[0])
# 0x627306090abaB3A6e1400e9345bC60c78a8BEf57
```

```{warning}
NEVER put a seed phrase with real funds here.
```
Expand Down
29 changes: 29 additions & 0 deletions src/ape/api/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
from ape.types.signatures import MessageSignature, SignableMessage
from ape.utils.basemodel import BaseInterfaceModel
from ape.utils.misc import raises_not_implemented
from ape.utils.testing import (
DEFAULT_NUMBER_OF_TEST_ACCOUNTS,
DEFAULT_TEST_HD_PATH,
DEFAULT_TEST_MNEMONIC,
)

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

@property
def mnemonic(self) -> str:
return self.config_manager.test.get("mnemonic", DEFAULT_TEST_MNEMONIC)

@mnemonic.setter
def mnemonic(self, value: str):
self.config_manager.test.mnemonic = value

@property
def number_of_accounts(self) -> int:
return self.config_manager.test.get("number_of_accounts", DEFAULT_NUMBER_OF_TEST_ACCOUNTS)

@number_of_accounts.setter
def number_of_accounts(self, value: int):
self.config_manager.test.number_of_accounts = value

@property
def hd_path(self) -> str:
return self.config_manager.test.get("hd_path", DEFAULT_TEST_HD_PATH)

@hd_path.setter
def hd_path(self, value: str):
self.config_manager.test.hd_path = value

@cached_property
def data_folder(self) -> Path:
"""
Expand Down
31 changes: 30 additions & 1 deletion src/ape/managers/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from eth_utils import is_hex

from ape.api.accounts import (
from ape.api import (
AccountAPI,
AccountContainerAPI,
ImpersonatedAccount,
Expand Down Expand Up @@ -53,6 +53,35 @@ def containers(self) -> dict[str, TestAccountContainerAPI]:
for plugin_name, (container_type, account_type) in account_types
}

@property
def mnemonic(self) -> str:
"""
The seed phrase for generated test accounts.
"""
return self.config_manager.get_config("test").mnemonic

@mnemonic.setter
def mnemonic(self, value: str):
"""
The seed phrase for generated test accounts.
**WARNING**: Changing the test-mnemonic mid-session
re-starts the provider (if connected to one).
"""
self.config_manager.test.mnemonic = value
self.containers["test"].mnemonic = value

if provider := self.network_manager.active_provider:
provider.update_settings({"mnemonic": value})

self._accounts_by_index = {}

@property
def number_of_accounts(self) -> int:
"""
The number of test accounts to generate and fund by default.
"""
return self.config_manager.test.number_of_accounts

@property
def hd_path(self) -> str:
"""
Expand Down
2 changes: 1 addition & 1 deletion src/ape/managers/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -1952,7 +1952,7 @@ def reconfigure(self, **overrides):
Change a project's config.
Args:
**overrides: Config key-value pairs. Completely overridesfe
**overrides: Config key-value pairs. Completely overrides
existing.
"""

Expand Down
4 changes: 2 additions & 2 deletions src/ape_accounts/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ def generate_account(
Args:
alias (str): The alias name of the account.
passphrase (str): Passphrase used to encrypt the account storage file.
hd_path (str): The hierarchal deterministic path to use when generating the account.
hd_path (str): The hierarchical deterministic path to use when generating the account.
Defaults to `m/44'/60'/0'/0/0`.
word_count (int): The amount of words to use in the generated mnemonic.
Expand Down Expand Up @@ -347,7 +347,7 @@ def import_account_from_mnemonic(
alias (str): The alias name of the account.
passphrase (str): Passphrase used to encrypt the account storage file.
mnemonic (str): List of space-separated words representing the mnemonic seed phrase.
hd_path (str): The hierarchal deterministic path to use when generating the account.
hd_path (str): The hierarchical deterministic path to use when generating the account.
Defaults to `m/44'/60'/0'/0/0`.
Returns:
Expand Down
31 changes: 15 additions & 16 deletions src/ape_test/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,7 @@
from ape.types.signatures import MessageSignature, TransactionSignature
from ape.utils._web3_compat import sign_hash
from ape.utils.misc import log_instead_of_fail
from ape.utils.testing import (
DEFAULT_NUMBER_OF_TEST_ACCOUNTS,
DEFAULT_TEST_HD_PATH,
DEFAULT_TEST_MNEMONIC,
generate_dev_accounts,
)
from ape.utils.testing import generate_dev_accounts

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

@property
def config(self):
return self.config_manager.get_config("test")

@property
def mnemonic(self) -> str:
return self.config.get("mnemonic", DEFAULT_TEST_MNEMONIC)
# Overridden so we can overload the setter.
return self.config_manager.test.mnemonic

@property
def number_of_accounts(self) -> int:
return self.config.get("number_of_accounts", DEFAULT_NUMBER_OF_TEST_ACCOUNTS)
@mnemonic.setter
def mnemonic(self, mnemonic: str) -> None:
# Overridden so we can also clear out generated accounts cache.
self.config_manager.test.mnemonic = mnemonic
self.generated_accounts = []

@TestAccountContainerAPI.mnemonic.setter
def mnemonic(self, mnemonic: str) -> None:
self.config_manager.test.mnemonic = mnemonic
self.generated_accounts = []

@property
def hd_path(self) -> str:
return self.config.get("hd_path", DEFAULT_TEST_HD_PATH)
def config(self):
return self.config_manager.get_config("test")

@property
def aliases(self) -> Iterator[str]:
Expand Down
29 changes: 19 additions & 10 deletions tests/functional/test_accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
)
from ape.types.gas import AutoGasLimit
from ape.types.signatures import recover_signer
from ape.utils.testing import DEFAULT_TEST_MNEMONIC
from ape_accounts.accounts import (
KeyfileAccount,
generate_account,
Expand Down Expand Up @@ -696,18 +697,26 @@ def test_using_different_hd_path(accounts, project, eth_tester_provider):
assert old_address != new_address


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

old_address = accounts[0].address
original_settings = eth_tester_provider.settings.model_dump(by_alias=True)
with project.temp_config(**test_config):
eth_tester_provider.update_settings(test_config["test"])
new_address = accounts[0].address

eth_tester_provider.update_settings(original_settings)
assert old_address != new_address
def test_mnemonic_setter(accounts):
original_mnemonic = accounts.mnemonic
new_mnemonic = "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat"
original_address = accounts[0].address

# Change.
accounts.mnemonic = new_mnemonic
new_address = accounts[0].address

# Put back.
accounts.mnemonic = original_mnemonic

# Assert.
assert new_address != original_address


def test_iter_test_accounts(accounts):
Expand Down

0 comments on commit db798c0

Please sign in to comment.