mm-btc 0.1.3__tar.gz → 0.2.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.
- {mm_btc-0.1.3 → mm_btc-0.2.0}/PKG-INFO +1 -2
- {mm_btc-0.1.3 → mm_btc-0.2.0}/pyproject.toml +1 -4
- {mm_btc-0.1.3 → mm_btc-0.2.0}/uv.lock +1 -39
- mm_btc-0.1.3/src/mm_btc/cli/cli.py +0 -79
- mm_btc-0.1.3/src/mm_btc/cli/cli_utils.py +0 -5
- mm_btc-0.1.3/src/mm_btc/cli/cmd/__init__.py +0 -0
- mm_btc-0.1.3/src/mm_btc/cli/cmd/address_cmd.py +0 -10
- mm_btc-0.1.3/src/mm_btc/cli/cmd/create_tx_cmd.py +0 -27
- mm_btc-0.1.3/src/mm_btc/cli/cmd/decode_tx_cmd.py +0 -8
- mm_btc-0.1.3/src/mm_btc/cli/cmd/mnemonic_cmd.py +0 -49
- mm_btc-0.1.3/src/mm_btc/cli/cmd/utxo_cmd.py +0 -11
- mm_btc-0.1.3/tests/__init__.py +0 -0
- mm_btc-0.1.3/tests/cli/cmd/test_mnemonic_cmd.py +0 -22
- {mm_btc-0.1.3 → mm_btc-0.2.0}/.env.example +0 -0
- {mm_btc-0.1.3 → mm_btc-0.2.0}/.gitignore +0 -0
- {mm_btc-0.1.3 → mm_btc-0.2.0}/README.txt +0 -0
- {mm_btc-0.1.3 → mm_btc-0.2.0}/justfile +0 -0
- {mm_btc-0.1.3 → mm_btc-0.2.0}/src/mm_btc/__init__.py +0 -0
- {mm_btc-0.1.3 → mm_btc-0.2.0}/src/mm_btc/blockstream.py +0 -0
- {mm_btc-0.1.3 → mm_btc-0.2.0}/src/mm_btc/py.typed +0 -0
- {mm_btc-0.1.3 → mm_btc-0.2.0}/src/mm_btc/tx.py +0 -0
- {mm_btc-0.1.3 → mm_btc-0.2.0}/src/mm_btc/wallet.py +0 -0
- {mm_btc-0.1.3/src/mm_btc/cli → mm_btc-0.2.0/tests}/__init__.py +0 -0
- {mm_btc-0.1.3 → mm_btc-0.2.0}/tests/conftest.py +0 -0
- {mm_btc-0.1.3 → mm_btc-0.2.0}/tests/test_blockstream.py +0 -0
- {mm_btc-0.1.3 → mm_btc-0.2.0}/tests/test_wallet.py +0 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mm-btc
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Requires-Python: >=3.12
|
|
5
5
|
Requires-Dist: bitcoinlib~=0.7.1
|
|
6
6
|
Requires-Dist: bit~=0.8.0
|
|
7
7
|
Requires-Dist: hdwallet~=3.2.0
|
|
8
8
|
Requires-Dist: mm-std~=0.1.9
|
|
9
9
|
Requires-Dist: mnemonic>=0.21
|
|
10
|
-
Requires-Dist: typer>=0.15.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mm-btc"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = ""
|
|
5
5
|
requires-python = ">=3.12"
|
|
6
6
|
dependencies = [
|
|
@@ -8,11 +8,8 @@ dependencies = [
|
|
|
8
8
|
"hdwallet~=3.2.0",
|
|
9
9
|
"bit~=0.8.0",
|
|
10
10
|
"bitcoinlib~=0.7.1",
|
|
11
|
-
"typer>=0.15.1",
|
|
12
11
|
"mnemonic>=0.21",
|
|
13
12
|
]
|
|
14
|
-
[project.scripts]
|
|
15
|
-
mm-btc = "mm_btc.cli.cli:app"
|
|
16
13
|
|
|
17
14
|
[build-system]
|
|
18
15
|
requires = ["hatchling"]
|
|
@@ -221,18 +221,6 @@ wheels = [
|
|
|
221
221
|
{ url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 },
|
|
222
222
|
]
|
|
223
223
|
|
|
224
|
-
[[package]]
|
|
225
|
-
name = "click"
|
|
226
|
-
version = "8.1.7"
|
|
227
|
-
source = { registry = "https://pypi.org/simple" }
|
|
228
|
-
dependencies = [
|
|
229
|
-
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
230
|
-
]
|
|
231
|
-
sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 }
|
|
232
|
-
wheels = [
|
|
233
|
-
{ url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 },
|
|
234
|
-
]
|
|
235
|
-
|
|
236
224
|
[[package]]
|
|
237
225
|
name = "coincurve"
|
|
238
226
|
version = "20.0.0"
|
|
@@ -564,7 +552,7 @@ wheels = [
|
|
|
564
552
|
|
|
565
553
|
[[package]]
|
|
566
554
|
name = "mm-btc"
|
|
567
|
-
version = "0.
|
|
555
|
+
version = "0.2.0"
|
|
568
556
|
source = { editable = "." }
|
|
569
557
|
dependencies = [
|
|
570
558
|
{ name = "bit" },
|
|
@@ -572,7 +560,6 @@ dependencies = [
|
|
|
572
560
|
{ name = "hdwallet" },
|
|
573
561
|
{ name = "mm-std" },
|
|
574
562
|
{ name = "mnemonic" },
|
|
575
|
-
{ name = "typer" },
|
|
576
563
|
]
|
|
577
564
|
|
|
578
565
|
[package.dev-dependencies]
|
|
@@ -593,7 +580,6 @@ requires-dist = [
|
|
|
593
580
|
{ name = "hdwallet", specifier = "~=3.2.0" },
|
|
594
581
|
{ name = "mm-std", specifier = "~=0.1.9" },
|
|
595
582
|
{ name = "mnemonic", specifier = ">=0.21" },
|
|
596
|
-
{ name = "typer", specifier = ">=0.15.1" },
|
|
597
583
|
]
|
|
598
584
|
|
|
599
585
|
[package.metadata.requires-dev]
|
|
@@ -1083,15 +1069,6 @@ wheels = [
|
|
|
1083
1069
|
{ url = "https://files.pythonhosted.org/packages/13/9f/026e18ca7d7766783d779dae5e9c656746c6ede36ef73c6d934aaf4a6dec/ruff-0.8.4-py3-none-win_arm64.whl", hash = "sha256:9183dd615d8df50defa8b1d9a074053891ba39025cf5ae88e8bcb52edcc4bf08", size = 9074500 },
|
|
1084
1070
|
]
|
|
1085
1071
|
|
|
1086
|
-
[[package]]
|
|
1087
|
-
name = "shellingham"
|
|
1088
|
-
version = "1.5.4"
|
|
1089
|
-
source = { registry = "https://pypi.org/simple" }
|
|
1090
|
-
sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 }
|
|
1091
|
-
wheels = [
|
|
1092
|
-
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 },
|
|
1093
|
-
]
|
|
1094
|
-
|
|
1095
1072
|
[[package]]
|
|
1096
1073
|
name = "six"
|
|
1097
1074
|
version = "1.16.0"
|
|
@@ -1178,21 +1155,6 @@ wheels = [
|
|
|
1178
1155
|
{ url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 },
|
|
1179
1156
|
]
|
|
1180
1157
|
|
|
1181
|
-
[[package]]
|
|
1182
|
-
name = "typer"
|
|
1183
|
-
version = "0.15.1"
|
|
1184
|
-
source = { registry = "https://pypi.org/simple" }
|
|
1185
|
-
dependencies = [
|
|
1186
|
-
{ name = "click" },
|
|
1187
|
-
{ name = "rich" },
|
|
1188
|
-
{ name = "shellingham" },
|
|
1189
|
-
{ name = "typing-extensions" },
|
|
1190
|
-
]
|
|
1191
|
-
sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/dca7b219718afd37a0068f4f2530a727c2b74a8b6e8e0c0080a4c0de4fcd/typer-0.15.1.tar.gz", hash = "sha256:a0588c0a7fa68a1978a069818657778f86abe6ff5ea6abf472f940a08bfe4f0a", size = 99789 }
|
|
1192
|
-
wheels = [
|
|
1193
|
-
{ url = "https://files.pythonhosted.org/packages/d0/cc/0a838ba5ca64dc832aa43f727bd586309846b0ffb2ce52422543e6075e8a/typer-0.15.1-py3-none-any.whl", hash = "sha256:7994fb7b8155b64d3402518560648446072864beefd44aa2dc36972a5972e847", size = 44908 },
|
|
1194
|
-
]
|
|
1195
|
-
|
|
1196
1158
|
[[package]]
|
|
1197
1159
|
name = "types-pyyaml"
|
|
1198
1160
|
version = "6.0.12.20241221"
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from typing import Annotated
|
|
3
|
-
|
|
4
|
-
import typer
|
|
5
|
-
import typer.core
|
|
6
|
-
from mm_std import print_plain
|
|
7
|
-
|
|
8
|
-
from mm_btc.cli import cli_utils
|
|
9
|
-
from mm_btc.cli.cmd import address_cmd, create_tx_cmd, decode_tx_cmd, mnemonic_cmd, utxo_cmd
|
|
10
|
-
from mm_btc.wallet import AddressType
|
|
11
|
-
|
|
12
|
-
app = typer.Typer(no_args_is_help=True, pretty_exceptions_enable=False, add_completion=False)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@app.command("mnemonic")
|
|
16
|
-
@app.command(name="m", hidden=True)
|
|
17
|
-
def mnemonic_command( # nosec B107:hardcoded_password_default
|
|
18
|
-
mnemonic: Annotated[str, typer.Option("--mnemonic", "-m", help="")] = "",
|
|
19
|
-
passphrase: Annotated[str, typer.Option("--passphrase", "-p")] = "",
|
|
20
|
-
path: Annotated[str, typer.Option("--path", help="Derivation path. Examples: bip44, bip88, m/44'/0'/0'/0")] = "bip44",
|
|
21
|
-
address_type: Annotated[AddressType, typer.Option("--address-type", "-a", help="Bitcoin address type")] = AddressType.P2WPKH,
|
|
22
|
-
hex_: Annotated[bool, typer.Option("--hex", help="Print private key in hex format instead of WIF")] = False,
|
|
23
|
-
words: int = typer.Option(12, "--words", "-w", help="Number of mnemonic words"),
|
|
24
|
-
limit: int = typer.Option(10, "--limit", "-l"),
|
|
25
|
-
testnet: bool = typer.Option(False, "--testnet", "-t", help="Testnet network"),
|
|
26
|
-
) -> None:
|
|
27
|
-
"""Generate keys based on a mnemonic"""
|
|
28
|
-
mnemonic_cmd.run(
|
|
29
|
-
mnemonic_cmd.Args(
|
|
30
|
-
mnemonic=mnemonic,
|
|
31
|
-
passphrase=passphrase,
|
|
32
|
-
words=words,
|
|
33
|
-
limit=limit,
|
|
34
|
-
path=path,
|
|
35
|
-
address_type=address_type,
|
|
36
|
-
hex=hex_,
|
|
37
|
-
testnet=testnet,
|
|
38
|
-
)
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
@app.command(name="address")
|
|
43
|
-
@app.command(name="a", hidden=True)
|
|
44
|
-
def address_command(address: str) -> None:
|
|
45
|
-
"""Get address info from Blockstream API"""
|
|
46
|
-
address_cmd.run(address)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
@app.command("create-tx")
|
|
50
|
-
def create_tx_command(config_path: Annotated[Path, typer.Argument(exists=True)]) -> None:
|
|
51
|
-
"""Create a transaction"""
|
|
52
|
-
create_tx_cmd.run(config_path)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@app.command("decode-tx")
|
|
56
|
-
def decode_tx_command(tx_hex: str, testnet: Annotated[bool, typer.Option("--testnet", "-t")] = False) -> None:
|
|
57
|
-
"""Decode a transaction"""
|
|
58
|
-
decode_tx_cmd.run(tx_hex, testnet)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
@app.command("utxo")
|
|
62
|
-
def utxo_command(address: str) -> None:
|
|
63
|
-
"""Get UTXOs from Blockstream API"""
|
|
64
|
-
utxo_cmd.run(address)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def version_callback(value: bool) -> None:
|
|
68
|
-
if value:
|
|
69
|
-
print_plain(f"mm-btc: v{cli_utils.get_version()}")
|
|
70
|
-
raise typer.Exit()
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
@app.callback()
|
|
74
|
-
def main(_version: bool = typer.Option(None, "--version", callback=version_callback, is_eager=True)) -> None:
|
|
75
|
-
pass
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if __name__ == "__main_":
|
|
79
|
-
app()
|
|
File without changes
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
from mm_std import print_json
|
|
2
|
-
|
|
3
|
-
from mm_btc.blockstream import BlockstreamClient
|
|
4
|
-
from mm_btc.wallet import is_testnet_address
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def run(address: str) -> None:
|
|
8
|
-
client = BlockstreamClient(testnet=is_testnet_address(address))
|
|
9
|
-
res = client.get_address(address)
|
|
10
|
-
print_json(res)
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
|
|
3
|
-
from bit import PrivateKey, PrivateKeyTestnet
|
|
4
|
-
from mm_std import BaseConfig, print_console
|
|
5
|
-
|
|
6
|
-
from mm_btc.wallet import is_testnet_address
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class Config(BaseConfig):
|
|
10
|
-
class Output(BaseConfig):
|
|
11
|
-
address: str
|
|
12
|
-
amount: int
|
|
13
|
-
|
|
14
|
-
from_address: str
|
|
15
|
-
private: str
|
|
16
|
-
outputs: list[Output]
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def run(config_path: Path) -> None:
|
|
20
|
-
config = Config.read_config(config_path)
|
|
21
|
-
testnet = is_testnet_address(config.from_address)
|
|
22
|
-
key = PrivateKeyTestnet(config.private) if testnet else PrivateKey(config.private)
|
|
23
|
-
|
|
24
|
-
outputs = [(o.address, o.amount, "satoshi") for o in config.outputs]
|
|
25
|
-
|
|
26
|
-
tx = key.create_transaction(outputs)
|
|
27
|
-
print_console(tx)
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from enum import Enum
|
|
3
|
-
|
|
4
|
-
from mm_std import print_plain
|
|
5
|
-
|
|
6
|
-
from mm_btc.wallet import AddressType, derive_accounts, generate_mnemonic
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class PrivateType(str, Enum):
|
|
10
|
-
hex = "hex"
|
|
11
|
-
wif = "wif"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@dataclass
|
|
15
|
-
class Args:
|
|
16
|
-
mnemonic: str
|
|
17
|
-
passphrase: str
|
|
18
|
-
words: int
|
|
19
|
-
limit: int
|
|
20
|
-
hex: bool # Print private key in hex format instead of WIF
|
|
21
|
-
address_type: AddressType
|
|
22
|
-
path: str
|
|
23
|
-
testnet: bool
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def run(args: Args) -> None:
|
|
27
|
-
mnemonic = args.mnemonic or generate_mnemonic()
|
|
28
|
-
passphrase = args.passphrase
|
|
29
|
-
path = get_derivation_path_prefix(args.path, args.testnet)
|
|
30
|
-
accounts = derive_accounts(mnemonic, passphrase, path, args.address_type, args.limit)
|
|
31
|
-
|
|
32
|
-
print_plain(f"{mnemonic}")
|
|
33
|
-
if passphrase:
|
|
34
|
-
print_plain(f"{passphrase}")
|
|
35
|
-
for acc in accounts:
|
|
36
|
-
private = acc.private if args.hex else acc.wif
|
|
37
|
-
print_plain(f"{acc.path} {acc.address} {private}")
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def get_derivation_path_prefix(path: str, testnet: bool) -> str:
|
|
41
|
-
if path.startswith("m/"):
|
|
42
|
-
return path
|
|
43
|
-
coin = "1" if testnet else "0"
|
|
44
|
-
if path == "bip44":
|
|
45
|
-
return f"m/44'/{coin}'/0'/0"
|
|
46
|
-
if path == "bip84":
|
|
47
|
-
return f"m/84'/{coin}'/0'/0"
|
|
48
|
-
|
|
49
|
-
raise ValueError("Invalid path")
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
from mm_std import print_json
|
|
2
|
-
|
|
3
|
-
from mm_btc.blockstream import BlockstreamClient
|
|
4
|
-
from mm_btc.wallet import is_testnet_address
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def run(address: str) -> None:
|
|
8
|
-
client = BlockstreamClient(testnet=is_testnet_address(address))
|
|
9
|
-
res = client.get_utxo(address)
|
|
10
|
-
|
|
11
|
-
print_json(res.ok_or_err())
|
mm_btc-0.1.3/tests/__init__.py
DELETED
|
File without changes
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
from mm_std import run_command
|
|
3
|
-
|
|
4
|
-
from mm_btc.cli.cmd.mnemonic_cmd import get_derivation_path_prefix
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def test_get_derivation_path_prefix():
|
|
8
|
-
assert get_derivation_path_prefix("m/11'/0'/0'/0", testnet=True) == "m/11'/0'/0'/0"
|
|
9
|
-
assert get_derivation_path_prefix("bip44", False) == "m/44'/0'/0'/0"
|
|
10
|
-
assert get_derivation_path_prefix("bip44", True) == "m/44'/1'/0'/0"
|
|
11
|
-
assert get_derivation_path_prefix("bip84", False) == "m/84'/0'/0'/0"
|
|
12
|
-
assert get_derivation_path_prefix("bip84", True) == "m/84'/1'/0'/0"
|
|
13
|
-
|
|
14
|
-
with pytest.raises(ValueError):
|
|
15
|
-
get_derivation_path_prefix("bip", True)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def test_mnemonic_cmd(mnemonic, passphrase):
|
|
19
|
-
cmd = f"mm-btc mnemonic -m '{mnemonic}' --passphrase '{passphrase}'"
|
|
20
|
-
res = run_command(cmd)
|
|
21
|
-
assert res.code == 0
|
|
22
|
-
assert "m/44'/0'/0'/0/7 1AJNF13C4xVvduE3D9pdtdDNFbL5Bm7T3u L3QF5FHUtX2a1ucGgVfrdhdvLHBSxsRQoGsv7tyY8P5Jt7UV9LZv"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|