eth-keypass 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 iamdefinitelyahuman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,242 @@
1
+ Metadata-Version: 2.4
2
+ Name: eth-keypass
3
+ Version: 0.1.0
4
+ Summary: EVM account management tool.
5
+ Author: iamdefinitelyahuman
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/iamdefinitelyahuman/eth-keypass
8
+ Project-URL: Repository, https://github.com/iamdefinitelyahuman/eth-keypass
9
+ Requires-Python: >=3.12
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: argon2-cffi>=23.1.0
13
+ Requires-Dist: cryptography>=42.0.0
14
+ Requires-Dist: platformdirs>=4.0
15
+ Requires-Dist: pycryptodome>=3.20
16
+ Provides-Extra: dev
17
+ Requires-Dist: pytest>=8; extra == "dev"
18
+ Requires-Dist: pytest-cov>=5; extra == "dev"
19
+ Requires-Dist: hypothesis>=6.0; extra == "dev"
20
+ Requires-Dist: eth-account<0.14,>=0.13.7; extra == "dev"
21
+ Requires-Dist: ruff>=0.9; extra == "dev"
22
+ Requires-Dist: pre-commit>=3; extra == "dev"
23
+ Requires-Dist: build; extra == "dev"
24
+ Requires-Dist: twine; extra == "dev"
25
+ Requires-Dist: mkdocs>=1.6; extra == "dev"
26
+ Requires-Dist: mkdocs-material>=9.5; extra == "dev"
27
+ Provides-Extra: docs
28
+ Requires-Dist: mkdocs>=1.6; extra == "docs"
29
+ Requires-Dist: mkdocs-material>=9.5; extra == "docs"
30
+ Dynamic: license-file
31
+
32
+ # eth-keypass
33
+
34
+ Simple EVM account management tool.
35
+
36
+ **NOTE**: This library is still in early alpha development. Prior to a `v1.0.0` (which may never come), expect breaking changes and no backward compatibility between versions.
37
+
38
+ ## Installation
39
+
40
+ You can install the latest release via `pip`:
41
+
42
+ ```bash
43
+ pip install eth_keypass
44
+ ```
45
+
46
+ Or clone the repository for the most up-to-date version:
47
+
48
+ ```bash
49
+ git clone https://github.com/iamdefinitelyahuman/eth-keypass.git
50
+ cd eth-keypass
51
+ pip install -e .
52
+ ```
53
+
54
+ ## Usage
55
+
56
+ ### 1. Create and access a Keypass
57
+
58
+ Creating a keypass is a single function call. The result is a persistent database stored on disk.
59
+
60
+ ```py
61
+ >>> import eth_keypass
62
+
63
+ >>> kp = eth_keypass.create("main")
64
+ Create password for new keypass 'main': ***
65
+
66
+ >>> kp
67
+ <KeypassDB path=/home/user/.local/share/eth-keypass/main.sqlite3 unlocked>
68
+ ```
69
+
70
+ Once created, it can be opened and unlocked in another session.
71
+
72
+ ```py
73
+ >>> import eth_keypass
74
+
75
+ >>> kp = eth_keypass.open("main")
76
+ >>> kp.unlock()
77
+ Enter the password for keypass 'main': ***
78
+
79
+ # alternatively use the top-level "unlock" to open and unlock in a single action
80
+ >>> kp = eth_keypass.unlock("main", password="mypassword")
81
+ ```
82
+
83
+ ### 2. Create an account
84
+
85
+ A new account can be generated directly inside the keypass. Its private key is encrypted and stored in the database.
86
+
87
+ ```py
88
+ >>> acct = kp.create_account()
89
+ >>> acct
90
+ <Account 0x0D3AaC9458167352493864Af54D1CBD7F1B8fF68 signable>
91
+ ```
92
+
93
+ ### 3. Import existing accounts
94
+
95
+ A keypass can hold multiple accounts.
96
+
97
+ You can import an existing account from a raw private key:
98
+
99
+ ```py
100
+ >>> kp.add_private_key(
101
+ ... "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
102
+ ... alias="imported-key",
103
+ ... )
104
+ <Account 0xFCAd0B19bB29D4674531d6f115237E16AfCE377c signable>
105
+ ```
106
+
107
+ Or import from an Ethereum keystore JSON:
108
+
109
+ ```py
110
+ >>> kp.import_keystore(
111
+ ... "./my-keystore.json",
112
+ ... password="keystore-password",
113
+ ... alias="imported-json",
114
+ ... )
115
+ <Account 0x104da57BF95262B119553181bA62dD5B8F204f49 signable>
116
+ ```
117
+
118
+ ### 4. Set aliases
119
+
120
+ You can set aliases for accounts to simplify retrieval later. A single account can have multiple aliases.
121
+
122
+ ```py
123
+ >>> kp.set_alias(acct, "hot")
124
+ >>> kp.set_alias(acct, "trading")
125
+
126
+ >>> kp["hot"]
127
+ <Account 0x0D3AaC9458167352493864Af54D1CBD7F1B8fF68 signable>
128
+
129
+ >>> kp["0x0D3AaC9458167352493864Af54D1CBD7F1B8fF68"]
130
+ <Account 0x0D3AaC9458167352493864Af54D1CBD7F1B8fF68 signable>
131
+
132
+ >>> kp["hot"] == kp["trading"] == acct
133
+ True
134
+ ```
135
+
136
+ ### 5. Add watch-only accounts
137
+
138
+ If you assign an alias to an address that has no private key in the keypass, it is stored as a watch-only account.
139
+
140
+ This lets you keep named references to important addresses and retrieve them later by alias.
141
+
142
+ ```py
143
+ >>> kp.set_alias("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "vitalik")
144
+
145
+ >>> kp["vitalik"]
146
+ <Account 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 watch-only>
147
+ ```
148
+
149
+ ### 6. Sign a transaction
150
+
151
+ Here we build a transaction from one of our signing accounts to send to one of our watch-only accounts.
152
+
153
+ ```py
154
+ >>> tx = {
155
+ ... "to": kp["vitalik"],
156
+ ... "value": 10**18,
157
+ ... "gas": 21_000,
158
+ ... "maxFeePerGas": 30_000_000_000,
159
+ ... "maxPriorityFeePerGas": 2_000_000_000,
160
+ ... "nonce": 0,
161
+ ... "chainId": 1
162
+ ... }
163
+ >>> signed = kp["hot"].sign_transaction(tx)
164
+
165
+ >>> signed
166
+ SignedTransaction(raw_transaction=b'\x02...', hash=b'\xa1...', r=..., s=..., v=1)
167
+ ```
168
+
169
+ ### 7. Sign messages
170
+
171
+ The `Account` object also exposes methods for signing messages and authorizations.
172
+
173
+ ```py
174
+ Account.sign_authorization(authorization_dict: dict[str, Any])
175
+ ```
176
+
177
+ ```py
178
+ Account.sign_message(signable_message: Any)
179
+ ```
180
+
181
+ ```py
182
+ Account.sign_typed_data(
183
+ domain_data: dict[str, Any] | None = None,
184
+ message_types: dict[str, Any] | None = None,
185
+ message_data: dict[str, Any] | None = None,
186
+ full_message: dict[str, Any] | None = None,
187
+ )
188
+ ```
189
+
190
+ All signing methods implement an equivalent API to the same-named functions in the [`eth-account`](https://github.com/ethereum/eth-account) library.
191
+
192
+ ### 8. Default keypass and accounts
193
+
194
+ You can create a "default" keypass by calling `create` with no arguments:
195
+
196
+ ```py
197
+ >>> eth_keypass.create()
198
+ Create password for new keypass 'default':
199
+ <KeypassDB path=/home/user/.local/share/eth-keypass/default.sqlite3 unlocked>
200
+ ```
201
+
202
+ Every keypass also maintains a default account.
203
+
204
+ ```py
205
+ >>> kp.default_account
206
+ eth_keypass.errors.NoDefaultAccountError: database has no default account set
207
+
208
+ >>> kp.create_account(alias="degen", set_as_default=True)
209
+ <Account 0xcc27b3Be484E3692737993ca8273349e9483454D signable>
210
+
211
+ >>> kp.default_account
212
+ <Account 0xcc27b3Be484E3692737993ca8273349e9483454D signable>
213
+ ```
214
+
215
+ This makes portable scripts possible. Each user signs using **their own default keypass and account**.
216
+
217
+ ```py
218
+ import eth_keypass
219
+
220
+ kp = eth_keypass.unlock()
221
+
222
+ acct = kp.default_account
223
+ acct.sign_transaction(...)
224
+ ```
225
+
226
+ ## Tests
227
+
228
+ First, install the dev dependencies:
229
+
230
+ ```bash
231
+ pip install -e ".[dev]"
232
+ ```
233
+
234
+ To run the test suite:
235
+
236
+ ```bash
237
+ pytest
238
+ ```
239
+
240
+ ## License
241
+
242
+ This project is licensed under the [MIT license](LICENSE).
@@ -0,0 +1,211 @@
1
+ # eth-keypass
2
+
3
+ Simple EVM account management tool.
4
+
5
+ **NOTE**: This library is still in early alpha development. Prior to a `v1.0.0` (which may never come), expect breaking changes and no backward compatibility between versions.
6
+
7
+ ## Installation
8
+
9
+ You can install the latest release via `pip`:
10
+
11
+ ```bash
12
+ pip install eth_keypass
13
+ ```
14
+
15
+ Or clone the repository for the most up-to-date version:
16
+
17
+ ```bash
18
+ git clone https://github.com/iamdefinitelyahuman/eth-keypass.git
19
+ cd eth-keypass
20
+ pip install -e .
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ### 1. Create and access a Keypass
26
+
27
+ Creating a keypass is a single function call. The result is a persistent database stored on disk.
28
+
29
+ ```py
30
+ >>> import eth_keypass
31
+
32
+ >>> kp = eth_keypass.create("main")
33
+ Create password for new keypass 'main': ***
34
+
35
+ >>> kp
36
+ <KeypassDB path=/home/user/.local/share/eth-keypass/main.sqlite3 unlocked>
37
+ ```
38
+
39
+ Once created, it can be opened and unlocked in another session.
40
+
41
+ ```py
42
+ >>> import eth_keypass
43
+
44
+ >>> kp = eth_keypass.open("main")
45
+ >>> kp.unlock()
46
+ Enter the password for keypass 'main': ***
47
+
48
+ # alternatively use the top-level "unlock" to open and unlock in a single action
49
+ >>> kp = eth_keypass.unlock("main", password="mypassword")
50
+ ```
51
+
52
+ ### 2. Create an account
53
+
54
+ A new account can be generated directly inside the keypass. Its private key is encrypted and stored in the database.
55
+
56
+ ```py
57
+ >>> acct = kp.create_account()
58
+ >>> acct
59
+ <Account 0x0D3AaC9458167352493864Af54D1CBD7F1B8fF68 signable>
60
+ ```
61
+
62
+ ### 3. Import existing accounts
63
+
64
+ A keypass can hold multiple accounts.
65
+
66
+ You can import an existing account from a raw private key:
67
+
68
+ ```py
69
+ >>> kp.add_private_key(
70
+ ... "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
71
+ ... alias="imported-key",
72
+ ... )
73
+ <Account 0xFCAd0B19bB29D4674531d6f115237E16AfCE377c signable>
74
+ ```
75
+
76
+ Or import from an Ethereum keystore JSON:
77
+
78
+ ```py
79
+ >>> kp.import_keystore(
80
+ ... "./my-keystore.json",
81
+ ... password="keystore-password",
82
+ ... alias="imported-json",
83
+ ... )
84
+ <Account 0x104da57BF95262B119553181bA62dD5B8F204f49 signable>
85
+ ```
86
+
87
+ ### 4. Set aliases
88
+
89
+ You can set aliases for accounts to simplify retrieval later. A single account can have multiple aliases.
90
+
91
+ ```py
92
+ >>> kp.set_alias(acct, "hot")
93
+ >>> kp.set_alias(acct, "trading")
94
+
95
+ >>> kp["hot"]
96
+ <Account 0x0D3AaC9458167352493864Af54D1CBD7F1B8fF68 signable>
97
+
98
+ >>> kp["0x0D3AaC9458167352493864Af54D1CBD7F1B8fF68"]
99
+ <Account 0x0D3AaC9458167352493864Af54D1CBD7F1B8fF68 signable>
100
+
101
+ >>> kp["hot"] == kp["trading"] == acct
102
+ True
103
+ ```
104
+
105
+ ### 5. Add watch-only accounts
106
+
107
+ If you assign an alias to an address that has no private key in the keypass, it is stored as a watch-only account.
108
+
109
+ This lets you keep named references to important addresses and retrieve them later by alias.
110
+
111
+ ```py
112
+ >>> kp.set_alias("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "vitalik")
113
+
114
+ >>> kp["vitalik"]
115
+ <Account 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 watch-only>
116
+ ```
117
+
118
+ ### 6. Sign a transaction
119
+
120
+ Here we build a transaction from one of our signing accounts to send to one of our watch-only accounts.
121
+
122
+ ```py
123
+ >>> tx = {
124
+ ... "to": kp["vitalik"],
125
+ ... "value": 10**18,
126
+ ... "gas": 21_000,
127
+ ... "maxFeePerGas": 30_000_000_000,
128
+ ... "maxPriorityFeePerGas": 2_000_000_000,
129
+ ... "nonce": 0,
130
+ ... "chainId": 1
131
+ ... }
132
+ >>> signed = kp["hot"].sign_transaction(tx)
133
+
134
+ >>> signed
135
+ SignedTransaction(raw_transaction=b'\x02...', hash=b'\xa1...', r=..., s=..., v=1)
136
+ ```
137
+
138
+ ### 7. Sign messages
139
+
140
+ The `Account` object also exposes methods for signing messages and authorizations.
141
+
142
+ ```py
143
+ Account.sign_authorization(authorization_dict: dict[str, Any])
144
+ ```
145
+
146
+ ```py
147
+ Account.sign_message(signable_message: Any)
148
+ ```
149
+
150
+ ```py
151
+ Account.sign_typed_data(
152
+ domain_data: dict[str, Any] | None = None,
153
+ message_types: dict[str, Any] | None = None,
154
+ message_data: dict[str, Any] | None = None,
155
+ full_message: dict[str, Any] | None = None,
156
+ )
157
+ ```
158
+
159
+ All signing methods implement an equivalent API to the same-named functions in the [`eth-account`](https://github.com/ethereum/eth-account) library.
160
+
161
+ ### 8. Default keypass and accounts
162
+
163
+ You can create a "default" keypass by calling `create` with no arguments:
164
+
165
+ ```py
166
+ >>> eth_keypass.create()
167
+ Create password for new keypass 'default':
168
+ <KeypassDB path=/home/user/.local/share/eth-keypass/default.sqlite3 unlocked>
169
+ ```
170
+
171
+ Every keypass also maintains a default account.
172
+
173
+ ```py
174
+ >>> kp.default_account
175
+ eth_keypass.errors.NoDefaultAccountError: database has no default account set
176
+
177
+ >>> kp.create_account(alias="degen", set_as_default=True)
178
+ <Account 0xcc27b3Be484E3692737993ca8273349e9483454D signable>
179
+
180
+ >>> kp.default_account
181
+ <Account 0xcc27b3Be484E3692737993ca8273349e9483454D signable>
182
+ ```
183
+
184
+ This makes portable scripts possible. Each user signs using **their own default keypass and account**.
185
+
186
+ ```py
187
+ import eth_keypass
188
+
189
+ kp = eth_keypass.unlock()
190
+
191
+ acct = kp.default_account
192
+ acct.sign_transaction(...)
193
+ ```
194
+
195
+ ## Tests
196
+
197
+ First, install the dev dependencies:
198
+
199
+ ```bash
200
+ pip install -e ".[dev]"
201
+ ```
202
+
203
+ To run the test suite:
204
+
205
+ ```bash
206
+ pytest
207
+ ```
208
+
209
+ ## License
210
+
211
+ This project is licensed under the [MIT license](LICENSE).
@@ -0,0 +1,64 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "eth-keypass"
7
+ version = "0.1.0"
8
+ description = "EVM account management tool."
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "iamdefinitelyahuman" }
14
+ ]
15
+
16
+ dependencies = [
17
+ "argon2-cffi>=23.1.0",
18
+ "cryptography>=42.0.0",
19
+ "platformdirs>=4.0",
20
+ "pycryptodome>=3.20",
21
+ ]
22
+
23
+ [project.optional-dependencies]
24
+ dev = [
25
+ "pytest>=8",
26
+ "pytest-cov>=5",
27
+ "hypothesis>=6.0",
28
+ "eth-account>=0.13.7,<0.14",
29
+ "ruff>=0.9",
30
+ "pre-commit>=3",
31
+ "build",
32
+ "twine",
33
+ "mkdocs>=1.6",
34
+ "mkdocs-material>=9.5",
35
+ ]
36
+ docs = [
37
+ "mkdocs>=1.6",
38
+ "mkdocs-material>=9.5",
39
+ ]
40
+
41
+ [project.urls]
42
+ Homepage = "https://github.com/iamdefinitelyahuman/eth-keypass"
43
+ Repository = "https://github.com/iamdefinitelyahuman/eth-keypass"
44
+
45
+ [tool.setuptools]
46
+ package-dir = {"" = "src"}
47
+
48
+ [tool.setuptools.packages.find]
49
+ where = ["src"]
50
+
51
+ [tool.ruff]
52
+ line-length = 100
53
+ target-version = "py312"
54
+
55
+ [tool.ruff.lint]
56
+ select = ["E", "F", "I"]
57
+
58
+ [tool.pytest.ini_options]
59
+ addopts = [
60
+ "--cov=eth_keypass",
61
+ "--cov-report=term-missing",
62
+ "--cov-report=xml",
63
+ ]
64
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,12 @@
1
+ """Public package interface for eth-keypass."""
2
+
3
+ from .account import Account
4
+ from .db import KeypassDB, create, open, unlock
5
+
6
+ __all__ = [
7
+ "Account",
8
+ "KeypassDB",
9
+ "create",
10
+ "open",
11
+ "unlock",
12
+ ]