mm-btc 0.5.5__py3-none-any.whl → 0.6.0__py3-none-any.whl

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/__init__.py CHANGED
@@ -0,0 +1 @@
1
+ """mm-btc: Bitcoin wallet, transaction, and blockchain utilities."""
mm_btc/blockstream.py CHANGED
@@ -1,3 +1,5 @@
1
+ """Blockstream Esplora API client for querying Bitcoin blockchain data."""
2
+
1
3
  from collections.abc import Sequence
2
4
 
3
5
  from mm_http import HttpResponse, http_request
@@ -5,18 +7,19 @@ from mm_result import Result
5
7
  from mm_web3 import random_proxy
6
8
  from pydantic import BaseModel
7
9
 
10
+ # Blockstream API base URLs.
8
11
  MAINNET_BASE_URL = "https://blockstream.info/api"
9
12
  TESTNET_BASE_URL = "https://blockstream.info/testnet/api"
10
13
 
11
- # ERROR_INVALID_ADDRESS = "INVALID_ADDRESS"
12
- # ERROR_INVALID_NETWORK = "INVALID_NETWORK"
13
-
14
+ # Error constant for HTTP 400 responses.
14
15
  ERROR_400_BAD_REQUEST = "400 Bad Request"
15
16
 
16
17
  type Proxy = str | Sequence[str] | None
17
18
 
18
19
 
19
20
  class Mempool(BaseModel):
21
+ """Bitcoin mempool summary statistics."""
22
+
20
23
  count: int
21
24
  vsize: int
22
25
  total_fee: int
@@ -24,7 +27,11 @@ class Mempool(BaseModel):
24
27
 
25
28
 
26
29
  class Address(BaseModel):
30
+ """Bitcoin address information with on-chain and mempool statistics."""
31
+
27
32
  class ChainStats(BaseModel):
33
+ """Confirmed on-chain transaction statistics for an address."""
34
+
28
35
  funded_txo_count: int
29
36
  funded_txo_sum: int
30
37
  spent_txo_count: int
@@ -32,6 +39,8 @@ class Address(BaseModel):
32
39
  tx_count: int
33
40
 
34
41
  class MempoolStats(BaseModel):
42
+ """Unconfirmed mempool transaction statistics for an address."""
43
+
35
44
  funded_txo_count: int
36
45
  funded_txo_sum: int
37
46
  spent_txo_count: int
@@ -43,7 +52,11 @@ class Address(BaseModel):
43
52
 
44
53
 
45
54
  class Utxo(BaseModel):
55
+ """Unspent transaction output."""
56
+
46
57
  class Status(BaseModel):
58
+ """Confirmation status of a UTXO."""
59
+
47
60
  confirmed: bool
48
61
  block_height: int
49
62
  block_hash: str
@@ -56,7 +69,10 @@ class Utxo(BaseModel):
56
69
 
57
70
 
58
71
  class BlockstreamClient:
72
+ """HTTP client for the Blockstream Esplora REST API."""
73
+
59
74
  def __init__(self, testnet: bool = False, timeout: float = 5, proxies: Proxy = None, attempts: int = 1) -> None:
75
+ """Initialize the client with network, timeout, proxy, and retry settings."""
60
76
  self.testnet = testnet
61
77
  self.timeout = timeout
62
78
  self.proxies = proxies
@@ -64,41 +80,46 @@ class BlockstreamClient:
64
80
  self.base_url = TESTNET_BASE_URL if testnet else MAINNET_BASE_URL
65
81
 
66
82
  async def get_address(self, address: str) -> Result[Address]:
83
+ """Fetch address information including chain and mempool stats."""
67
84
  result: Result[Address] = Result.err("not started yet")
68
85
  for _ in range(self.attempts):
69
86
  res = await self._request(f"/address/{address}")
70
87
  try:
71
88
  if res.status_code == 400:
72
89
  return res.to_result_err("400 Bad Request")
73
- return res.to_result_ok(Address(**res.parse_json()))
90
+ return res.to_result_ok(Address(**res.json_body().unwrap()))
74
91
  except Exception as e:
75
92
  result = res.to_result_err(e)
76
93
  return result
77
94
 
78
95
  async def get_confirmed_balance(self, address: str) -> Result[int]:
96
+ """Fetch the confirmed balance (funded minus spent) for an address."""
79
97
  return (await self.get_address(address)).chain(
80
98
  lambda a: Result.ok(a.chain_stats.funded_txo_sum - a.chain_stats.spent_txo_sum)
81
99
  )
82
100
 
83
101
  async def get_utxo(self, address: str) -> Result[list[Utxo]]:
102
+ """Fetch the list of UTXOs for an address."""
84
103
  result: Result[list[Utxo]] = Result.err("not started yet")
85
104
  for _ in range(self.attempts):
86
105
  res = await self._request(f"/address/{address}/utxo")
87
106
  try:
88
- return res.to_result_ok([Utxo(**out) for out in res.parse_json()])
107
+ return res.to_result_ok([Utxo(**out) for out in res.json_body().unwrap()])
89
108
  except Exception as e:
90
109
  result = res.to_result_err(e)
91
110
  return result
92
111
 
93
112
  async def get_mempool(self) -> Result[Mempool]:
113
+ """Fetch current mempool summary statistics."""
94
114
  result: Result[Mempool] = Result.err("not started yet")
95
115
  for _ in range(self.attempts):
96
116
  res = await self._request("/mempool")
97
117
  try:
98
- return res.to_result_ok(Mempool(**res.parse_json()))
118
+ return res.to_result_ok(Mempool(**res.json_body().unwrap()))
99
119
  except Exception as e:
100
120
  result = res.to_result_err(e)
101
121
  return result
102
122
 
103
123
  async def _request(self, url: str) -> HttpResponse:
124
+ """Send an HTTP GET request to the Blockstream API."""
104
125
  return await http_request(f"{self.base_url}{url}", timeout=self.timeout, proxy=random_proxy(self.proxies))
mm_btc/cli/__init__.py CHANGED
@@ -0,0 +1 @@
1
+ """CLI package for mm-btc."""
mm_btc/cli/cli.py CHANGED
@@ -1,10 +1,12 @@
1
+ """CLI entry point for mm-btc commands."""
2
+
1
3
  import asyncio
2
4
  import importlib.metadata
3
5
  from pathlib import Path
4
6
  from typing import Annotated
5
7
 
6
- import mm_print
7
8
  import typer
9
+ from mm_print import print_plain
8
10
 
9
11
  from mm_btc.wallet import AddressType
10
12
 
@@ -15,7 +17,7 @@ app = typer.Typer(no_args_is_help=True, pretty_exceptions_enable=False, add_comp
15
17
 
16
18
  @app.command("mnemonic")
17
19
  @app.command(name="m", hidden=True)
18
- def mnemonic_command( # nosec B107:hardcoded_password_default
20
+ def mnemonic_command( # nosec B107:hardcoded_password_default — empty string is a safe default, actual passphrase is user-provided
19
21
  mnemonic: Annotated[str, typer.Option("--mnemonic", "-m", help="")] = "",
20
22
  passphrase: Annotated[str, typer.Option("--passphrase", "-p")] = "",
21
23
  path: Annotated[str, typer.Option("--path", help="Derivation path. Examples: bip44, bip88, m/44'/0'/0'/0")] = "bip44",
@@ -66,15 +68,16 @@ def utxo_command(address: str) -> None:
66
68
 
67
69
 
68
70
  def version_callback(value: bool) -> None:
71
+ """Print the version and exit when --version flag is passed."""
69
72
  if value:
70
- mm_print.plain(f"mm-btc: v{importlib.metadata.version('mm-btc')}")
73
+ print_plain(f"mm-btc: v{importlib.metadata.version('mm-btc')}")
71
74
  raise typer.Exit
72
75
 
73
76
 
74
77
  @app.callback()
75
78
  def main(_version: bool = typer.Option(None, "--version", callback=version_callback, is_eager=True)) -> None:
76
- pass
79
+ """mm-btc CLI application root callback."""
77
80
 
78
81
 
79
- if __name__ == "__main_":
82
+ if __name__ == "__main__":
80
83
  app()
@@ -0,0 +1 @@
1
+ """CLI command handlers for mm-btc."""
@@ -1,10 +1,13 @@
1
- import mm_print
1
+ """CLI handler for the address command — fetches address info from Blockstream."""
2
+
3
+ from mm_print import print_json
2
4
 
3
5
  from mm_btc.blockstream import BlockstreamClient
4
6
  from mm_btc.wallet import is_testnet_address
5
7
 
6
8
 
7
9
  async def run(address: str) -> None:
10
+ """Fetch and print address information from the Blockstream API."""
8
11
  client = BlockstreamClient(testnet=is_testnet_address(address))
9
12
  res = await client.get_address(address)
10
- mm_print.json(res.value_or_error())
13
+ print_json(res.value_or_error())
@@ -1,14 +1,20 @@
1
+ """CLI handler for the create-tx command — builds a Bitcoin transaction from a TOML config."""
2
+
1
3
  from pathlib import Path
2
4
 
3
- import mm_print
4
5
  from bit import PrivateKey, PrivateKeyTestnet
6
+ from mm_print import print_json
5
7
  from mm_web3 import Web3CliConfig
6
8
 
7
9
  from mm_btc.wallet import is_testnet_address
8
10
 
9
11
 
10
12
  class Config(Web3CliConfig):
13
+ """Transaction creation configuration loaded from a TOML file."""
14
+
11
15
  class Output(Web3CliConfig):
16
+ """Single transaction output with destination address and amount in satoshis."""
17
+
12
18
  address: str
13
19
  amount: int
14
20
 
@@ -18,6 +24,7 @@ class Config(Web3CliConfig):
18
24
 
19
25
 
20
26
  def run(config_path: Path) -> None:
27
+ """Build and print a signed Bitcoin transaction from the given config file."""
21
28
  config = Config.read_toml_config_or_exit(config_path)
22
29
  testnet = is_testnet_address(config.from_address)
23
30
  key = PrivateKeyTestnet(config.private) if testnet else PrivateKey(config.private)
@@ -25,4 +32,4 @@ def run(config_path: Path) -> None:
25
32
  outputs = [(o.address, o.amount, "satoshi") for o in config.outputs]
26
33
 
27
34
  tx = key.create_transaction(outputs)
28
- mm_print.json(tx)
35
+ print_json(tx)
@@ -1,8 +1,11 @@
1
- import mm_print
1
+ """CLI handler for the decode-tx command — decodes a raw transaction hex."""
2
+
3
+ from mm_print import print_json
2
4
 
3
5
  from mm_btc.tx import decode_tx
4
6
 
5
7
 
6
8
  def run(tx_hex: str, testnet: bool = False) -> None:
9
+ """Decode and print a raw Bitcoin transaction."""
7
10
  res = decode_tx(tx_hex, testnet)
8
- mm_print.json(res)
11
+ print_json(res)
@@ -1,18 +1,24 @@
1
+ """CLI handler for the mnemonic command — generates and displays HD wallet keys."""
2
+
1
3
  from dataclasses import dataclass
2
- from enum import Enum
4
+ from enum import StrEnum
3
5
 
4
- import mm_print
6
+ from mm_print import print_plain
5
7
 
6
8
  from mm_btc.wallet import AddressType, derive_accounts, generate_mnemonic
7
9
 
8
10
 
9
- class PrivateType(str, Enum):
11
+ class PrivateType(StrEnum):
12
+ """Output format for private keys."""
13
+
10
14
  hex = "hex"
11
15
  wif = "wif"
12
16
 
13
17
 
14
18
  @dataclass
15
19
  class Args:
20
+ """Arguments for the mnemonic command."""
21
+
16
22
  mnemonic: str
17
23
  passphrase: str
18
24
  words: int
@@ -24,20 +30,22 @@ class Args:
24
30
 
25
31
 
26
32
  def run(args: Args) -> None:
33
+ """Execute the mnemonic command: generate or use a mnemonic and print derived accounts."""
27
34
  mnemonic = args.mnemonic or generate_mnemonic()
28
35
  passphrase = args.passphrase
29
36
  path = get_derivation_path_prefix(args.path, args.testnet)
30
37
  accounts = derive_accounts(mnemonic, passphrase, path, args.address_type, args.limit)
31
38
 
32
- mm_print.plain(f"{mnemonic}")
39
+ print_plain(f"{mnemonic}")
33
40
  if passphrase:
34
- mm_print.plain(f"{passphrase}")
41
+ print_plain(f"{passphrase}")
35
42
  for acc in accounts:
36
43
  private = acc.private if args.hex else acc.wif
37
- mm_print.plain(f"{acc.path} {acc.address} {private}")
44
+ print_plain(f"{acc.path} {acc.address} {private}")
38
45
 
39
46
 
40
47
  def get_derivation_path_prefix(path: str, testnet: bool) -> str:
48
+ """Resolve a path alias (bip44, bip84) or custom path to a full derivation path prefix."""
41
49
  if path.startswith("m/"):
42
50
  return path
43
51
  coin = "1" if testnet else "0"
@@ -1,10 +1,13 @@
1
- import mm_print
1
+ """CLI handler for the utxo command — fetches UTXOs from Blockstream."""
2
+
3
+ from mm_print import print_json
2
4
 
3
5
  from mm_btc.blockstream import BlockstreamClient
4
6
  from mm_btc.wallet import is_testnet_address
5
7
 
6
8
 
7
9
  async def run(address: str) -> None:
10
+ """Fetch and print UTXOs for an address from the Blockstream API."""
8
11
  client = BlockstreamClient(testnet=is_testnet_address(address))
9
12
  res = await client.get_utxo(address)
10
- mm_print.json(res.value_or_error())
13
+ print_json(res.value_or_error())
mm_btc/tx.py CHANGED
@@ -1,5 +1,8 @@
1
+ """Bitcoin transaction decoding utilities."""
2
+
1
3
  from bitcoinlib.transactions import Transaction
2
4
 
3
5
 
4
6
  def decode_tx(tx_hex: str, testnet: bool = False) -> dict[str, object]:
5
- return Transaction.parse(tx_hex, network="testnet" if testnet else "mainnet").as_dict() # type: ignore[no-any-return]
7
+ """Decode a raw Bitcoin transaction hex string into a dictionary."""
8
+ return Transaction.parse(tx_hex, network="testnet" if testnet else "mainnet").as_dict() # type: ignore[no-any-return] # ty: ignore[unused-ignore-comment] # bitcoinlib has no type stubs, as_dict() returns Any
mm_btc/wallet.py CHANGED
@@ -1,5 +1,7 @@
1
+ """Bitcoin wallet operations: mnemonic generation, HD key derivation, and address utilities."""
2
+
1
3
  from dataclasses import dataclass
2
- from enum import Enum, unique
4
+ from enum import StrEnum, unique
3
5
 
4
6
  from hdwallet import HDWallet
5
7
  from hdwallet.cryptocurrencies import Bitcoin
@@ -8,6 +10,7 @@ from hdwallet.hds import BIP32HD
8
10
  from hdwallet.mnemonics import BIP39Mnemonic
9
11
  from mnemonic import Mnemonic
10
12
 
13
+ # BIP44/BIP84 standard derivation path prefixes for mainnet and testnet.
11
14
  BIP44_MAINNET_PATH = "m/44'/0'/0'/0"
12
15
  BIP44_TESTNET_PATH = "m/44'/1'/0'/0"
13
16
  BIP84_MAINNET_PATH = "m/84'/0'/0'/0"
@@ -16,6 +19,8 @@ BIP84_TESTNET_PATH = "m/84'/1'/0'/0"
16
19
 
17
20
  @dataclass
18
21
  class Account:
22
+ """Derived Bitcoin account with address, private key, WIF, and derivation path."""
23
+
19
24
  address: str
20
25
  private: str
21
26
  wif: str
@@ -23,7 +28,9 @@ class Account:
23
28
 
24
29
 
25
30
  @unique
26
- class AddressType(str, Enum):
31
+ class AddressType(StrEnum):
32
+ """Supported Bitcoin address types."""
33
+
27
34
  P2PKH = "P2PKH" # Pay to Public Key Hash
28
35
  P2SH = "P2SH" # Pay to Script Hash
29
36
  P2TR = "P2TR" # Taproot
@@ -34,10 +41,12 @@ class AddressType(str, Enum):
34
41
 
35
42
 
36
43
  def generate_mnemonic(language: str = "english", words: int = 12) -> str:
37
- return Mnemonic(language).generate(strength=mnemonic_words_to_strenght(words))
44
+ """Generate a BIP39 mnemonic phrase."""
45
+ return Mnemonic(language).generate(strength=mnemonic_words_to_strength(words))
38
46
 
39
47
 
40
48
  def derive_accounts(mnemonic: str, passphrase: str, path_prefix: str, address_type: AddressType, limit: int) -> list[Account]:
49
+ """Derive multiple Bitcoin accounts from a mnemonic using HD wallet derivation."""
41
50
  coin = Bitcoin
42
51
  if path_prefix.startswith(("m/84'/1'", "m/44'/1'")):
43
52
  network = coin.NETWORKS.TESTNET
@@ -56,13 +65,19 @@ def derive_accounts(mnemonic: str, passphrase: str, path_prefix: str, address_ty
56
65
  wallet.clean_derivation()
57
66
  path = f"{path_prefix}/{index_path}"
58
67
  w = wallet.from_derivation(derivation=CustomDerivation(path=path))
59
- accounts.append(Account(address=w.address(address_type), private=w.private_key(), wif=w.wif(), path=path))
68
+ address = w.address(address_type)
69
+ private = w.private_key()
70
+ wif = w.wif()
71
+ if address is None or private is None or wif is None:
72
+ raise ValueError(f"Failed to derive wallet data for path: {path}")
73
+ accounts.append(Account(address=address, private=private, wif=wif, path=path))
60
74
  w.clean_derivation()
61
75
 
62
76
  return accounts
63
77
 
64
78
 
65
- def mnemonic_words_to_strenght(words: int) -> int:
79
+ def mnemonic_words_to_strength(words: int) -> int:
80
+ """Convert mnemonic word count to BIP39 entropy strength in bits."""
66
81
  if words == 12:
67
82
  return 128
68
83
  if words == 15:
@@ -78,4 +93,5 @@ def mnemonic_words_to_strenght(words: int) -> int:
78
93
 
79
94
 
80
95
  def is_testnet_address(address: str) -> bool:
96
+ """Check if a Bitcoin address belongs to testnet based on its prefix."""
81
97
  return address.startswith(("m", "n", "tb1"))
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: mm-btc
3
+ Version: 0.6.0
4
+ Requires-Python: >=3.14
5
+ Requires-Dist: bitcoinlib~=0.7.6
6
+ Requires-Dist: bit~=0.8.0
7
+ Requires-Dist: hdwallet~=3.6.1
8
+ Requires-Dist: mm-web3~=0.6.2
9
+ Requires-Dist: mnemonic~=0.21
10
+ Requires-Dist: typer~=0.21.1
@@ -0,0 +1,17 @@
1
+ mm_btc/__init__.py,sha256=nJvUJnW252HgBF__F_DgOYDNI-tUgAiZR149DNlpzWg,69
2
+ mm_btc/blockstream.py,sha256=WE6XAvq25quUZ-EZJsTyA0Fc6jYkLtSJTY2gaF4MsaA,4247
3
+ mm_btc/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ mm_btc/tx.py,sha256=v90gGuJoss6Vdu40qLkbkouYfeEVgL-Gg8Ko-KBB94g,457
5
+ mm_btc/wallet.py,sha256=YfF9URrLg2f7zTRX7yOV8gX00un7VRLFMmDWeYmFDWY,3128
6
+ mm_btc/cli/__init__.py,sha256=QCh2qW3rj_5Ndi7lOxM47D7ILS8k6V0I01s-4cJscik,30
7
+ mm_btc/cli/cli.py,sha256=PDDijm0BFRYs4JeZA_bzhaPHQqnfoqsoTWfKCgPLucI,2863
8
+ mm_btc/cli/cmd/__init__.py,sha256=TzLfqMlXwZLIivl7NZ4r26uYe3NCfRxhgc0Ml9hWgJ0,39
9
+ mm_btc/cli/cmd/address_cmd.py,sha256=BTdZ0LpqHjHrW4Ot3HtU6aScZdzRsxGcW2XVclJr91s,473
10
+ mm_btc/cli/cmd/create_tx_cmd.py,sha256=WMdxw-YBDkXdzLQGE47Wwhk4Wh_hyhwFRzNoAxOwS2Q,1069
11
+ mm_btc/cli/cmd/decode_tx_cmd.py,sha256=2Lwnh2XzfsJSYMSAVyHMo9-s3p6d2IIUzbW1lJFvCGY,311
12
+ mm_btc/cli/cmd/mnemonic_cmd.py,sha256=dQftPqCvoWbiX7F2CROiOcHE0W69NHGvEhjev07KZE4,1624
13
+ mm_btc/cli/cmd/utxo_cmd.py,sha256=5qPejTeoXRPXw6MjApqHwj5GG6TJUf6XZuqPbwPg5To,461
14
+ mm_btc-0.6.0.dist-info/METADATA,sha256=cR3vKfFP1IGd4oW5e21LwnqktcYWxYWxiNvaOLdjWMI,253
15
+ mm_btc-0.6.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
+ mm_btc-0.6.0.dist-info/entry_points.txt,sha256=KphzLNE9eb9H1DO-L0gvBQaQT5ImedyVCN4a6svfiRg,46
17
+ mm_btc-0.6.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,10 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: mm-btc
3
- Version: 0.5.5
4
- Requires-Python: >=3.13
5
- Requires-Dist: bitcoinlib~=0.7.5
6
- Requires-Dist: bit~=0.8.0
7
- Requires-Dist: hdwallet~=3.6.1
8
- Requires-Dist: mm-web3~=0.5.4
9
- Requires-Dist: mnemonic~=0.21
10
- Requires-Dist: typer~=0.20.0
@@ -1,17 +0,0 @@
1
- mm_btc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- mm_btc/blockstream.py,sha256=i1s1kyJ_sGQ5X_jypIQcVrJuIFtS6O4NwkLoJaMSZPg,3336
3
- mm_btc/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- mm_btc/tx.py,sha256=7KTMNuL1n8rRj_245BV1bH9M2_PctNvlvPsLEsRTYx4,245
5
- mm_btc/wallet.py,sha256=_pGTuAf6Y9KzaWTTSg_tn6WEoRsGqhxJuPof0_xyrmM,2333
6
- mm_btc/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- mm_btc/cli/cli.py,sha256=vmrvmOxjyl6mSkz-OUOTn5pmJ_emRS_pCM1ZAaF6SsA,2626
8
- mm_btc/cli/cmd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- mm_btc/cli/cmd/address_cmd.py,sha256=8gzARLHv7wVg30SAgK9JDiZbJK0e0YFus4b2Y5NGey0,302
10
- mm_btc/cli/cmd/create_tx_cmd.py,sha256=VWTW7f8IRxisPWQAddw5fw9CRup2A6OWAJHKEpecLpc,714
11
- mm_btc/cli/cmd/decode_tx_cmd.py,sha256=oNOVX1_XIBdj0rY7ZMiwdNLVBvBHdnK0niJsXSulxXo,164
12
- mm_btc/cli/cmd/mnemonic_cmd.py,sha256=N3qaQfoivODj4EQIFn4fN0ULZon7rmoETqJPy3YnKbg,1248
13
- mm_btc/cli/cmd/utxo_cmd.py,sha256=6xGzi6RhXXUOgvNAFNWMWmO9AIDRWOVh_0X-jaCdeM4,299
14
- mm_btc-0.5.5.dist-info/METADATA,sha256=VdG3hHfAHyso9d2jUKCuaWeqZhcmgknSnA8dBCa-GA8,253
15
- mm_btc-0.5.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- mm_btc-0.5.5.dist-info/entry_points.txt,sha256=KphzLNE9eb9H1DO-L0gvBQaQT5ImedyVCN4a6svfiRg,46
17
- mm_btc-0.5.5.dist-info/RECORD,,