Skip to content

Commit ecb8fba

Browse files
authored
Add wallet.json support
Closes #19 Adds a utility function for decrypting a private key from a wallet.json file.
2 parents 61da237 + d1ca24c commit ecb8fba

8 files changed

Lines changed: 102 additions & 11 deletions

File tree

.github/workflows/example.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,13 @@ jobs:
5656
5757
- name: Create and fund an account
5858
run: |
59-
go run ./cmd/golembase account create
60-
go run ./cmd/golembase account fund
59+
printf "password" | go run ./cmd/golembase account create
60+
printf "password" | go run ./cmd/golembase account fund
6161
working-directory: ./gb-op-geth
6262

6363
- name: Run the example SDK app
6464
run: >
65-
nix develop --no-write-lock-file --reference-lock-file ../flake.lock --command
65+
printf "password" | nix develop --no-write-lock-file --reference-lock-file ../flake.lock --command
6666
./result/bin/main --instance local
6767
working-directory: ./example
6868

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ The repo also contains an example application to showcase how you can use this S
2020

2121
(Note: As an alternative to installing the demo CLI, you can build the [actual CLI](https://github.com/Golem-Base/golembase-op-geth/blob/main/cmd/golembase/README.md) as it's included in the golembase-op-geth repo.)
2222

23-
When you create a user, it will generate a private key file called `private.key` and store it in:
23+
When you create a user, it will generate a private key file called `wallet.json` and store it in:
2424

2525
* `~/.config/golembase/` on **Linux**
2626
* `~/Library/Application Support/golembase/` on **macOS**

example/golem_base_sdk_example/__init__.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@
55
import logging
66
import logging.config
77

8-
import anyio
98
from golem_base_sdk import (
109
Annotation,
1110
GolemBaseClient,
1211
GolemBaseCreate,
1312
GolemBaseDelete,
1413
GolemBaseExtend,
1514
GolemBaseUpdate,
15+
WalletError,
16+
decrypt_wallet,
1617
)
17-
from xdg import BaseDirectory
1818

1919
logging.config.dictConfig(
2020
{
@@ -59,11 +59,12 @@
5959

6060
async def run_example(instance: str) -> None: # noqa: PLR0915
6161
"""Run the example."""
62-
async with await anyio.open_file(
63-
BaseDirectory.xdg_config_home + "/golembase/private.key",
64-
"rb",
65-
) as private_key_file:
66-
key_bytes = await private_key_file.read(32)
62+
try:
63+
key_bytes = await decrypt_wallet()
64+
except WalletError as e:
65+
print(f"Error: {e}")
66+
except KeyboardInterrupt:
67+
print("\nOperation cancelled by user.")
6768

6869
client = await GolemBaseClient.create(
6970
rpc_url=INSTANCE_URLS[instance]["rpc"],

golem_base_sdk/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@
5151
WatchLogsHandle,
5252
)
5353
from .utils import rlp_encode_transaction
54+
from .wallet import (
55+
WalletError,
56+
decrypt_wallet,
57+
)
5458

5559
__all__: Sequence[str] = [
5660
# Exports from .types
@@ -73,6 +77,9 @@
7377
# Exports from .constants
7478
"GOLEM_BASE_ABI",
7579
"STORAGE_ADDRESS",
80+
# Exports from .wallet
81+
"decrypt_wallet",
82+
"WalletError",
7683
# Exports from this file
7784
"GolemBaseClient",
7885
# Re-exports

golem_base_sdk/wallet.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""Wallet module for creating and decrypting Ethereum wallets."""
2+
3+
import getpass
4+
import json
5+
import sys
6+
from pathlib import Path
7+
from typing import cast
8+
9+
import anyio
10+
from eth_account import Account
11+
from xdg import BaseDirectory
12+
13+
WALLET_PATH = Path(BaseDirectory.xdg_config_home) / "golembase" / "wallet.json"
14+
15+
class WalletError(Exception):
16+
"""Base class for wallet-related errors."""
17+
18+
pass
19+
20+
async def decrypt_wallet() -> bytes:
21+
"""Decrypts the wallet and returns the private key bytes."""
22+
if not WALLET_PATH.exists():
23+
raise WalletError(f"Expected wallet file to exist at '{WALLET_PATH}'")
24+
25+
async with await anyio.open_file(
26+
WALLET_PATH,
27+
"r",
28+
) as f:
29+
keyfile_json = json.loads(await f.read())
30+
31+
if not sys.stdin.isatty():
32+
password = sys.stdin.read().rstrip()
33+
else:
34+
password = getpass.getpass("Enter password to decrypt wallet: ")
35+
36+
try:
37+
print(f"Attempting to decrypt wallet at '{WALLET_PATH}'")
38+
private_key = Account.decrypt(keyfile_json, password)
39+
print("Successfully decrypted wallet")
40+
except ValueError as e:
41+
raise WalletError("Incorrect password or corrupted wallet file.") from e
42+
43+
return cast(bytes, private_key)
44+

nix/devshell.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ perSystem.devshell.mkShell {
1212
packages = [
1313
virtualenvDev
1414
pkgs.uv
15+
pkgs.ruff
1516
];
1617

1718
env = [

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ requires-python = ">=3.12"
1414
dynamic = ["description"]
1515

1616
dependencies = [
17+
"anyio>=4.10.0",
18+
"pyxdg>=0.28",
1719
"rlp>=1.2.0",
1820
"web3>=4.7.2",
1921
]

uv.lock

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)