-
Notifications
You must be signed in to change notification settings - Fork 0
added keystore based private keys #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,12 +29,24 @@ | |
|
|
||
| from __future__ import annotations | ||
|
|
||
| import getpass | ||
| from dataclasses import dataclass | ||
| from typing import Any, Optional | ||
|
|
||
| from eth_account import Account | ||
|
|
||
| from lume.client import LumeClient | ||
| from lume.constants import SIGNATURE_TYPE_POLY_GNOSIS_SAFE, get_config_with_env_overrides | ||
| from lume.exceptions import GraphQLError | ||
| from lume.utils import ( | ||
| DEFAULT_KEYSTORE_PATH, | ||
| decrypt_private_key, | ||
| encrypt_private_key, | ||
| generate_wallet, | ||
| keystore_exists, | ||
| load_keystore, | ||
| save_keystore, | ||
| ) | ||
|
|
||
|
|
||
| class AgentRegistrationError(GraphQLError): | ||
|
|
@@ -326,38 +338,108 @@ def verify_authentication(self) -> bool: | |
| return False | ||
|
|
||
|
|
||
| def _resolve_private_key_interactive(keystore_path: str) -> str: | ||
| """Resolve a private key interactively via keystore. | ||
|
|
||
| If a keystore file exists at *keystore_path*, the user is prompted for the | ||
| password (up to 3 attempts). Otherwise a new wallet is generated, the user | ||
| is asked to create a password (with confirmation), and the encrypted | ||
| keystore is persisted. | ||
|
|
||
| Returns: | ||
| Hex private key **without** the ``0x`` prefix. | ||
|
|
||
| Raises: | ||
| ValueError: After 3 failed password attempts or mismatched confirmation. | ||
| """ | ||
|
|
||
| if keystore_exists(keystore_path): | ||
| keystore = load_keystore(keystore_path) | ||
| for attempt in range(1, 4): | ||
| password = getpass.getpass( | ||
| f"Enter keystore password ({attempt}/3): " | ||
| ) | ||
| try: | ||
| private_key = decrypt_private_key(keystore, password) | ||
| address = Account.from_key(private_key).address | ||
| print(f"Wallet loaded: {address}") | ||
| print(f"Keystore: {keystore_path}") | ||
| return private_key | ||
| except ValueError: | ||
| if attempt < 3: | ||
| print("Incorrect password, try again.") | ||
| raise ValueError("Failed to decrypt keystore after 3 attempts") | ||
|
|
||
| # No existing keystore — generate a new wallet | ||
| private_key, address = generate_wallet() | ||
|
|
||
| password = getpass.getpass("Create keystore password: ") | ||
| password_confirm = getpass.getpass("Confirm keystore password: ") | ||
| if password != password_confirm: | ||
| raise ValueError("Passwords do not match") | ||
|
|
||
| keystore = encrypt_private_key(private_key, password) | ||
| saved_path = save_keystore(keystore, keystore_path) | ||
| print(f"New wallet generated: {address}") | ||
| print(f"Keystore saved: {saved_path}") | ||
| return private_key | ||
|
|
||
|
|
||
| def create_agent_from_env( | ||
| agent_id: Optional[str] = None, | ||
| display_name: Optional[str] = None, | ||
| auto_register: bool = True, | ||
| keystore_path: Optional[str] = None, | ||
| use_keystore: bool = True, | ||
| ) -> AgentClient: | ||
| """Create an AgentClient from environment variables. | ||
|
|
||
| Reads configuration from environment: | ||
| - LUME_PRIVATE_KEY or PRIVATE_KEY: Required wallet private key | ||
| - LUME_PRIVATE_KEY or PRIVATE_KEY: Wallet private key | ||
| - LUME_API_URL: Optional API URL override | ||
| - LUME_AGENT_ID: Optional agent ID (overridden by agent_id param) | ||
| - LUME_AGENT_NAME: Optional display name (overridden by display_name param) | ||
| - LUME_KEYSTORE_PATH: Optional custom keystore file path | ||
|
|
||
| **Priority chain** for resolving the private key: | ||
|
|
||
| 1. ``LUME_PRIVATE_KEY`` / ``PRIVATE_KEY`` environment variable | ||
| 2. Encrypted keystore file (if *use_keystore* is ``True``) | ||
| 3. Generate a new wallet and save to keystore | ||
|
|
||
| Args: | ||
| agent_id: Agent identifier (defaults to LUME_AGENT_ID env var) | ||
| display_name: Display name (defaults to LUME_AGENT_NAME env var) | ||
| auto_register: Whether to register automatically | ||
| keystore_path: Path to keystore JSON file (defaults to | ||
| ``LUME_KEYSTORE_PATH`` env var or ``~/.lume/keystore.json``) | ||
| use_keystore: If ``True`` (default) and no env-var key is present, | ||
| interactively resolve the key via keystore. If ``False``, | ||
| a missing env var raises ``ValueError``. | ||
|
|
||
| Returns: | ||
| Configured AgentClient instance | ||
|
|
||
| Raises: | ||
| ValueError: If PRIVATE_KEY is not set | ||
| ValueError: If no private key can be resolved | ||
| AgentRegistrationError: If auto_register is True and registration fails | ||
| """ | ||
| import os | ||
|
|
||
| private_key = os.getenv("LUME_PRIVATE_KEY") or os.getenv("PRIVATE_KEY") | ||
|
|
||
| if not private_key: | ||
| raise ValueError( | ||
| "LUME_PRIVATE_KEY or PRIVATE_KEY environment variable is required" | ||
| ) | ||
| if use_keystore: | ||
| resolved_path = ( | ||
| keystore_path | ||
| or os.getenv("LUME_KEYSTORE_PATH") | ||
| or DEFAULT_KEYSTORE_PATH | ||
| ) | ||
| private_key = _resolve_private_key_interactive(resolved_path) | ||
| else: | ||
| raise ValueError( | ||
| "LUME_PRIVATE_KEY or PRIVATE_KEY environment variable is required" | ||
| ) | ||
|
Comment on lines
388
to
+442
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Expand
🛠️ Suggested adjustment if use_keystore:
resolved_path = (
keystore_path
or os.getenv("LUME_KEYSTORE_PATH")
or DEFAULT_KEYSTORE_PATH
)
+ resolved_path = os.path.expanduser(os.path.expandvars(resolved_path))
private_key = _resolve_private_key_interactive(resolved_path)🤖 Prompt for AI Agents |
||
|
|
||
| api_url = os.getenv("LUME_API_URL") | ||
| resolved_agent_id = agent_id or os.getenv("LUME_AGENT_ID") | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,10 +2,16 @@ | |
|
|
||
| from __future__ import annotations | ||
|
|
||
| import json | ||
| import os | ||
| from decimal import Decimal, ROUND_DOWN | ||
| from pathlib import Path | ||
|
|
||
| from eth_account import Account | ||
|
|
||
| DEFAULT_KEYSTORE_DIR: str = os.path.join(str(Path.home()), ".lume") | ||
| DEFAULT_KEYSTORE_PATH: str = os.path.join(DEFAULT_KEYSTORE_DIR, "keystore.json") | ||
|
|
||
|
|
||
| def generate_wallet() -> tuple[str, str]: | ||
| """Generate a new Ethereum private key and address. | ||
|
|
@@ -51,3 +57,86 @@ def from_atomic(amount: int) -> Decimal: | |
| Decimal value (e.g. ``Decimal('1.500000')``). | ||
| """ | ||
| return Decimal(amount) / Decimal("1000000") | ||
|
|
||
|
|
||
| def encrypt_private_key(private_key: str, password: str) -> dict: | ||
| """Encrypt a private key using Ethereum keystore v3 format. | ||
|
|
||
| Args: | ||
| private_key: Hex private key (with or without 0x prefix). | ||
| password: Password to encrypt with. | ||
|
|
||
| Returns: | ||
| Keystore v3 JSON-compatible dict. | ||
| """ | ||
| if not private_key.startswith("0x"): | ||
| private_key = "0x" + private_key | ||
| return Account.encrypt(private_key, password) | ||
|
|
||
|
|
||
| def decrypt_private_key(keystore: dict, password: str) -> str: | ||
| """Decrypt a private key from an Ethereum keystore v3 dict. | ||
|
|
||
| Args: | ||
| keystore: Keystore v3 dict (as returned by :func:`encrypt_private_key`). | ||
| password: Password used during encryption. | ||
|
|
||
| Returns: | ||
| Hex private key *without* the ``0x`` prefix. | ||
|
|
||
| Raises: | ||
| ValueError: If the password is incorrect. | ||
| """ | ||
| try: | ||
| raw = Account.decrypt(keystore, password) | ||
| return raw.hex() | ||
| except Exception as exc: | ||
| raise ValueError("Incorrect password or corrupted keystore") from exc | ||
|
|
||
|
|
||
| def save_keystore(keystore: dict, path: str = DEFAULT_KEYSTORE_PATH) -> str: | ||
| """Persist a keystore dict to disk with restrictive permissions. | ||
|
|
||
| Creates the parent directory (mode 0o700) if it does not exist and | ||
| writes the file with mode 0o600 so only the current user can read it. | ||
|
|
||
| Args: | ||
| keystore: Keystore v3 dict. | ||
| path: Destination file path. | ||
|
|
||
| Returns: | ||
| The absolute path where the keystore was saved. | ||
| """ | ||
| directory = os.path.dirname(path) | ||
| os.makedirs(directory, mode=0o700, exist_ok=True) | ||
| with open(path, "w") as f: | ||
| json.dump(keystore, f, indent=2) | ||
| os.chmod(path, 0o600) | ||
| return os.path.abspath(path) | ||
|
Comment on lines
+97
to
+115
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Harden keystore saving for empty directories and restrictive perms at creation. If 🛠️ Proposed fix def save_keystore(keystore: dict, path: str = DEFAULT_KEYSTORE_PATH) -> str:
@@
- directory = os.path.dirname(path)
- os.makedirs(directory, mode=0o700, exist_ok=True)
- with open(path, "w") as f:
- json.dump(keystore, f, indent=2)
- os.chmod(path, 0o600)
+ directory = os.path.dirname(path)
+ if directory:
+ os.makedirs(directory, mode=0o700, exist_ok=True)
+ fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
+ with os.fdopen(fd, "w") as f:
+ json.dump(keystore, f, indent=2)
return os.path.abspath(path)🤖 Prompt for AI Agents |
||
|
|
||
|
|
||
| def load_keystore(path: str = DEFAULT_KEYSTORE_PATH) -> dict | None: | ||
| """Load a keystore dict from disk. | ||
|
|
||
| Args: | ||
| path: Path to the keystore JSON file. | ||
|
|
||
| Returns: | ||
| The parsed keystore dict, or ``None`` if the file does not exist. | ||
| """ | ||
| if not os.path.isfile(path): | ||
| return None | ||
| with open(path) as f: | ||
| return json.load(f) | ||
|
|
||
|
|
||
| def keystore_exists(path: str = DEFAULT_KEYSTORE_PATH) -> bool: | ||
| """Check whether a keystore file exists at *path*. | ||
|
|
||
| Args: | ||
| path: Path to check. | ||
|
|
||
| Returns: | ||
| ``True`` if the file exists, ``False`` otherwise. | ||
| """ | ||
| return os.path.isfile(path) | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -66,50 +66,69 @@ You can override individual settings with environment variables: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `LUME_ENV`, `LUME_API_URL`, `LUME_CHAIN_ID`, `LUME_CTF_EXCHANGE_ADDRESS`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `LUME_NEGRISK_EXCHANGE_ADDRESS`, `LUME_FEE_RATE_BPS`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Initialize the agent | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Initialize the agent — IMPORTANT: Follow this procedure | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| For autonomous agents, use `AgentClient` (extends `LumeClient` with | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| registration and authentication): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Before ANY trading operation, you MUST resolve the agent's private key. Follow | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this priority chain **in order** — stop at the first one that succeeds: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```python | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from lume import AgentClient | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #### Priority 1: Encrypted keystore (highest) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| agent = AgentClient(private_key=os.environ["PRIVATE_KEY"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| info = agent.register() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Check if `~/.lume/keystore.json` exists (or `LUME_KEYSTORE_PATH` env var). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Since `getpass` does not work in non-interactive shells, you MUST ask the user | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for their password using `AskUserQuestion`, then decrypt programmatically: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print(f"EOA Address: {agent.eoa_address}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print(f"Proxy Wallet: {agent.proxy_wallet}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print(f"Registered: {info.is_registered}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```python | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from lume import keystore_exists, load_keystore, decrypt_private_key | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if keystore_exists(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # keystore found — ASK the user for their password via AskUserQuestion, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # then decrypt: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| keystore = load_keystore() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private_key = decrypt_private_key(keystore, password) # password from user | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Or use the environment helper: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| If decryption fails (`ValueError`), tell the user the password was wrong and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ask again (up to 3 attempts). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```python | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from lume import create_agent_from_env | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #### Priority 2: Environment variable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| If no keystore exists, check `LUME_PRIVATE_KEY` or `PRIVATE_KEY`: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Reads LUME_PRIVATE_KEY (or PRIVATE_KEY), LUME_API_URL, LUME_AGENT_ID, LUME_AGENT_NAME | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| agent = create_agent_from_env(agent_id="my-bot", display_name="My Trading Bot") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```python | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private_key = os.getenv("LUME_PRIVATE_KEY") or os.getenv("PRIVATE_KEY") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| You can also use `LumeClient` directly if you don't need agent registration: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #### Priority 3: Generate new wallet | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| If neither keystore nor env var is available, generate a new wallet, ask the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user to create a password (with confirmation), encrypt, and save: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```python | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from lume import LumeClient | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from lume import generate_wallet, encrypt_private_key, save_keystore | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| client = LumeClient(private_key=os.environ["PRIVATE_KEY"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private_key, address = generate_wallet() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # ASK user to provide a password via AskUserQuestion | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| keystore = encrypt_private_key(private_key, password) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| save_keystore(keystore) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Generate a new wallet (if needed) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #### Create the AgentClient | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Once you have the `private_key`, create the agent directly: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```python | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from lume import generate_wallet | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from lume import AgentClient | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private_key, address = generate_wallet() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print(f"Address: {address}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Save private_key securely | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| agent = AgentClient(private_key=private_key, auto_register=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print(f"EOA Address: {agent.eoa_address}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print(f"Proxy Wallet: {agent.proxy_wallet}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > **Note:** Do NOT use `create_agent_from_env()` from within this skill because | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > its `getpass` prompts fail in non-interactive shells. Always resolve the key | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > yourself using the procedure above, then pass it to `AgentClient` directly. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+69
to
+130
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Align the snippet with Line 76 says ✏️ Suggested doc fix-from lume import keystore_exists, load_keystore, decrypt_private_key
+import os
+from lume import (
+ DEFAULT_KEYSTORE_PATH,
+ keystore_exists,
+ load_keystore,
+ decrypt_private_key,
+)
+
+keystore_path = os.getenv("LUME_KEYSTORE_PATH") or DEFAULT_KEYSTORE_PATH
-if keystore_exists():
+if keystore_exists(keystore_path):
# keystore found — ASK the user for their password via AskUserQuestion,
# then decrypt:
- keystore = load_keystore()
+ keystore = load_keystore(keystore_path)
private_key = decrypt_private_key(keystore, password) # password from user📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Key Concepts | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Price format: 1e6 atomic units | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Normalize keystore load failures to a consistent
ValueError.Right now, corrupt JSON or permission issues bubble as
JSONDecodeError/PermissionError, while docs mentionValueError. Consider wrapping keystore load failures to produce a consistent error.🛠️ Suggested adjustment
🤖 Prompt for AI Agents