diff --git a/bot/account.py b/bot/account.py index da2641c..610c197 100644 --- a/bot/account.py +++ b/bot/account.py @@ -101,7 +101,7 @@ class Account: # In case if there is a hanging logged in session self.logout() - def login(self, password: str, key_uid: Optional[str] = None, display_name: Optional[str] = None, mnemonic: Optional[str] = None, infura_token: Optional[str] = None, alchemy_token: Optional[str] = None, coingecko_api_key: Optional[str] = None): + def login(self, password: str, key_uid: Optional[str] = None, name: Optional[str] = None, mnemonic: Optional[str] = None, infura_token: Optional[str] = None, alchemy_token: Optional[str] = None, coingecko_api_key: Optional[str] = None): """ Login to the given account. If it does not exist, it will be created and automatically logged in. @@ -109,20 +109,20 @@ class Account: Parameters: - `password` - your Status password - `key_uid` - your key unique identifier. If not provided `display_name` will be used to fetch it. This means that each `display_name` can be linked to one `key_uid` - - `display_name` - your Status display name. Use `display_name` and `password` parameter combination if you have a 1 to 1 mapping (each display name has a unique `key_uid`) + - `name` - your Status display name or ENS. Use `name` and `password` parameter combination if you have a 1 to 1 mapping (ENS has a unique `key_uid`) - `mnemonic` - the mnemonic when creating an account. Use this field with `password` and `display_name` to recover an account - `infura_token` - https://www.infura.io/ RPC token to allow Status Backend to use a wallet - `alchemy_token` - https://alchemy.com/ RPC token to allow Status Backend to use a wallet - `coingecko_api_key` - https://www.coingecko.com/ API key to allow Status Backend to use a wallet """ - if not key_uid and not display_name: + if not key_uid and not name: raise exceptions.InvalidContactError() available_accounts = self.available_accounts - # Login combination: display_name + password + # Login combination: display_name (or ENS) + password if not key_uid: for account in available_accounts: - if account["display_name"] != display_name: + if account["name"] != name: continue key_uid = account["key_uid"] @@ -131,7 +131,7 @@ class Account: else: available_key_uids = [current["key_uid"] for current in available_accounts] if key_uid not in available_key_uids: - info = "\n".join([f"{current['key_uid']} - {current['display_name']}" for current in self.available_accounts]) + info = "\n".join([f"{current['key_uid']} - {current['name']}" for current in self.available_accounts]) raise exceptions.InvalidContactError(f"Given Key Unique Identifier is invalid...\nAvailable Key Unique Identifiers:\n{info}") is_new_account = isinstance(key_uid, type(None)) @@ -145,11 +145,11 @@ class Account: } if is_new_account or is_recovery: - self.__validate_display_name(display_name) + self.__validate_display_name(name) params = { "rootDataDir": self.__docker_data_folder, "kdfIterations": self.__kd_iterations, - "displayName": display_name, + "displayName": name, "password": password, "customizationColor": "primary", "wakuV2LightClient": False, @@ -159,7 +159,7 @@ class Account: self.logger.info(f"Logging in with Key UID - {key_uid}") if is_new_account: - self.logger.info(f"Creating account with display_name {display_name}") + self.logger.info(f"Creating account with display_name {name}") url_key = "create" if is_recovery: @@ -202,6 +202,7 @@ class Account: self.logger.info("Successfully logged in!") event: dict = signal_event["event"]["settings"] + ens_info: list[dict] = signal_event["event"].get("ensUsernames", []) self.__info = { "public_key": event["public-key"], "url": None, @@ -213,6 +214,10 @@ class Account: "bio": event.get("bio", ""), "password": password, "wallet_address": event["dapps-address"], + "ens": { + "preferred_name": event.get("preferred-name"), + "usernames": ens_info + }, "logged_in_timestamp": datetime.datetime.now() } self.__info["url"] = self.__call_rpc("urls", "shareUserURLWithData", [event["public-key"]]).get("result") @@ -255,7 +260,8 @@ class Account: current_available_accounts = [ { - "display_name": account["name"], + "name": account["name"], + "is_ens": account["name"].endswith(".eth"), "key_uid": account["key-uid"], "created_at": datetime.datetime.fromtimestamp(account["timestamp"]) } @@ -1226,7 +1232,7 @@ class Account: params = { "filePath": os.path.join(self.__docker_backup_folder, file_name).replace("\\", "/") } - self.logger.info(f"Trying to load {file_path}") + self.logger.info(f"Loading backup file: {file_path}") response = requests.post(self.__urls["http"]["load_backup"], json=params) error: str = response.json().get("error", "") diff --git a/docs/account.md b/docs/account.md index 6d41abe..f15994f 100644 --- a/docs/account.md +++ b/docs/account.md @@ -143,23 +143,23 @@ An account can also be recovered if the [`mnemonic`](https://status.app/help/pro |-----|-----|-----|-------------| | `password` | `str` | Yes | Password used to encrypt the account | | `key_uid` | `str` | Yes* | Unique key identifier of the account. If provided, the account will be logged in directly using this identifier. If not provided, then you must use `display_name` and `password` to login. | -| `display_name` | `str` | Yes* | Display name of the account. Used to resolve the `key_uid` if it is not provided, or to create a new account if one does not already exist. This field is required if an account needs to be recovered with `mnemonic`. | +| `name` | `str` | Yes* | Display name or [ENS name](https://status.app/help/profile/transfer-your-ens-name-to-status) of the account. Used to resolve the `key_uid` if it is not provided, or to create a new account if one does not already exist. This field is required if an account needs to be recovered with `mnemonic`. | | `mnemonic` | `str` | No | The [mnemonic](https://status.app/help/profile/understand-your-status-keys-and-recovery-phrase#about-your-recovery-phrase) from [`info`](./account.md#info). Use this field with `password` and `display_name` to recover the account. If you have [`.bkp`](./account.md#backup) files, in the backup Docker volume they will be automatically picked up and loaded.

**Note**: You can pass a different `display_name` but that will be internal only. When an account is recovered setting [`display_name`](./account.md#display_name) can be buggy. Ideally when recovering the account, use the original `display_name` of the account. | | `infura_token` | `str` | No | [RPC token](https://www.infura.io/) used by Status Backend for the Ethereum RPC component of the wallet. | | `alchemy_token` | `str` | No | Used to fetch [wallet transactions](./account.md#get_transactionsrefreshfalse) to fetch wallet transaction history via the Alchemy REST API, so no separate key is needed for transactions. | | `coingecko_api_key` | `str` | No | [API key](https://www.coingecko.com/) used by Status Backend to fetch token prices. | -Wallet functionality is split into three components, each backed by a token: Ethereum RPC (`infura_token`), transactions (`alchemy_token`) and prices (`coingecko_api_key`). All three must be provided for wallet RPC methods to work — if any is missing, wallet calls raise a `WalletNotConfiguredError`. +Wallet functionality is split into three components, each backed by a token: Ethereum RPC (`infura_token`), transactions (`alchemy_token`) and prices (`coingecko_api_key`). All three must be provided for wallet RPC methods to work - if any is missing, wallet calls raise a `WalletNotConfiguredError`. Returns the current `Account` instance, allowing method chaining. -#### Login with `display_name` +#### Login with Display name ```python from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -171,6 +171,25 @@ The code above is equivalent to the following screen on Status App: **Note**: This assumes that `display_name` and is unique for every `key_uid`. If there are duplicated `display_names` then the first found match will be used. You can log in with `key_uid` if you have `display_name` duplicates. +#### Login with ENS + +```python +from bot import Account + +account = Account() +params = { + "name": "malte.stateofus.eth", + "password": "SNTPUMP" +} +account.login(**params) +``` + +You can purchase a **universal username** on Status App: + +![ENS purchase](./images/ens.png) + + + #### Login with `key_uid` ```python @@ -191,7 +210,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP", "mnemonic" : "phrase_1 phrase_2 phrase_3 phrase_4 phrase_5 phrase_6 phrase_7 phrase_8 phrase_9 phrase_10 phrase_11 phrase_12" } @@ -212,7 +231,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP", "infura_token": "token from https://www.infura.io/", "alchemy_token": "token from https://www.alchemy.com/", @@ -221,7 +240,7 @@ params = { account.login(**params) ``` -**Note**: `infura_token`, `alchemy_token` and `coingecko_api_key` can be used when creating, recovering and logging in to an account. All three are required to enable wallet functionality — if any is missing, those calls raise a `WalletNotConfiguredError`. +**Note**: `infura_token`, `alchemy_token` and `coingecko_api_key` can be used when creating, recovering and logging in to an account. All three are required to enable wallet functionality - if any is missing, those calls raise a `WalletNotConfiguredError`. ### `logout()` @@ -232,7 +251,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -258,7 +277,7 @@ from bot import Account account = Account(backup_folder=r"C:\\Users\\me\\status-backups") params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -283,7 +302,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -315,7 +334,7 @@ import datetime account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -346,7 +365,7 @@ from rich.pretty import Pretty account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -384,7 +403,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -427,7 +446,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -457,7 +476,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -491,7 +510,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP", "infura_token": "token from https://www.infura.io/", "alchemy_token": "token from https://www.alchemy.com/", @@ -524,7 +543,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP", "infura_token": "token from https://www.infura.io/", "alchemy_token": "token from https://www.alchemy.com/", @@ -551,7 +570,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP", "infura_token": "token from https://www.infura.io/", "alchemy_token": "token from https://www.alchemy.com/", @@ -579,7 +598,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP", "infura_token": "token from https://www.infura.io/", "alchemy_token": "token from https://www.alchemy.com/", @@ -611,7 +630,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP", "infura_token": "token from https://www.infura.io/", "alchemy_token": "token from https://www.alchemy.com/", @@ -690,7 +709,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP", "infura_token": "token from https://www.infura.io/", "alchemy_token": "token from https://www.alchemy.com/", @@ -729,7 +748,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP", "infura_token": "token from https://www.infura.io/", "alchemy_token": "token from https://www.alchemy.com/", @@ -755,7 +774,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP", "infura_token": "token from https://www.infura.io/", "alchemy_token": "token from https://www.alchemy.com/", @@ -780,7 +799,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP", "infura_token": "token from https://www.infura.io/", "alchemy_token": "token from https://www.alchemy.com/", @@ -806,7 +825,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP", "infura_token": "token from https://www.infura.io/", "alchemy_token": "token from https://www.alchemy.com/", @@ -846,7 +865,14 @@ This property is useful when you want to: **You will have to know the passwords for the given `key_uid`.** -Returns `list[dict]`. +Returns `list[dict]`, one entry per locally available account. + +| Key | Type | Description | +|----|----|-------------| +| `name` | `str` | The account's name. For accounts using an [ENS](https://status.app/help/profile/transfer-your-ens-name-to-status) name, this is the ENS name (e.g. `malte.stateofus.eth`). For accounts that do not have an ENS name, this will be their display name. | +| `is_ens` | `bool` | `True` when `display_name` is an ENS name (ends with `.eth`), otherwise `False`. Useful for telling apart plain display names from universal usernames. | +| `key_uid` | `str` | Internal Status key identifier for the account. Can be passed to [`login`](./account.md#loginpassword-key_uidnone-display_namenone-mnemonicnone-infura_tokennone-alchemy_tokennone-coingecko_api_keynone) as `key_uid`. | +| `created_at` | `datetime.datetime` | Timestamp when the account was created locally. | ```python from bot import Account @@ -874,6 +900,7 @@ Provides information about the currently logged-in account. If `login()` has not | `display_name` | `str` | Display name of the account. | | `password` | `str` | Password used to encrypt the account locally. | | `wallet_address` | `str` | Ethereum wallet address associated with the account. | +| `ens` | `dict` | The account's [ENS](https://status.app/help/profile/transfer-your-ens-name-to-status) details. Contains `preferred_name` (`str` or `None`) - the ENS name the account has chosen to display - and `usernames` (`list[dict]`) - every ENS username registered to the account. Both are empty / `None` when no ENS name is set. | | `logged_in_timestamp` | `datetime.datetime` | Timestamp when the account successfully logged in. | ```python @@ -881,7 +908,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -900,7 +927,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -916,7 +943,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -939,7 +966,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -955,7 +982,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -972,7 +999,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -994,7 +1021,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -1012,7 +1039,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -1123,7 +1150,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -1191,7 +1218,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -1228,7 +1255,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -1257,7 +1284,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP" } account.login(**params) @@ -1288,7 +1315,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP", "infura_token": "token from https://www.infura.io/", "alchemy_token": "token from https://www.alchemy.com/", @@ -1318,7 +1345,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP", "infura_token": "token from https://www.infura.io/", "alchemy_token": "token from https://www.alchemy.com/", @@ -1336,7 +1363,7 @@ from bot import Account account = Account() params = { - "display_name": "status-app-bot", + "name": "status-app-bot", "password": "SNTPUMP", "infura_token": "token from https://www.infura.io/", "alchemy_token": "token from https://www.alchemy.com/", diff --git a/docs/images/ens.png b/docs/images/ens.png new file mode 100644 index 0000000..55e8eb4 Binary files /dev/null and b/docs/images/ens.png differ