mm-btc 0.0.2__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 ADDED
File without changes
mm_btc/blockstream.py ADDED
@@ -0,0 +1,63 @@
1
+ from collections.abc import Sequence
2
+ from typing import TypeAlias
3
+
4
+ from mm_std import Err, Ok, Result, hr
5
+ from mm_std.random_ import random_str_choice
6
+ from pydantic import BaseModel
7
+
8
+ MAINNET_BASE_URL = "https://blockstream.info/api"
9
+ TESTNET_BASE_URL = "https://blockstream.info/testnet/api"
10
+
11
+ ERROR_INVALID_ADDRESS = "INVALID_ADDRESS"
12
+ ERROR_INVALID_NETWORK = "INVALID_NETWORK"
13
+
14
+ Proxy: TypeAlias = str | Sequence[str] | None
15
+
16
+
17
+ class Address(BaseModel):
18
+ class ChainStats(BaseModel):
19
+ funded_txo_count: int
20
+ funded_txo_sum: int
21
+ spent_txo_count: int
22
+ spent_txo_sum: int
23
+ tx_count: int
24
+
25
+ class MempoolStats(BaseModel):
26
+ funded_txo_count: int
27
+ funded_txo_sum: int
28
+ spent_txo_count: int
29
+ spent_txo_sum: int
30
+ tx_count: int
31
+
32
+ chain_stats: ChainStats
33
+ mempool_stats: MempoolStats
34
+
35
+
36
+ def get_address(
37
+ address: str, testnet: bool = False, timeout: int = 10, proxies: Proxy = None, attempts: int = 1
38
+ ) -> Result[Address]:
39
+ result: Result[Address] = Err("not started yet")
40
+ data = None
41
+ base_url = TESTNET_BASE_URL if testnet else MAINNET_BASE_URL
42
+ for _ in range(attempts):
43
+ try:
44
+ res = hr(f"{base_url}/address/{address}", timeout=timeout, proxy=random_str_choice(proxies))
45
+ data = res.to_dict()
46
+ if res.code == 400 and (
47
+ "invalid bitcoin address" in res.body.lower() or "bech32 segwit decoding error" in res.body.lower()
48
+ ):
49
+ return Err(ERROR_INVALID_ADDRESS, data=data)
50
+ elif res.code == 400 and "invalid network" in res.body.lower():
51
+ return Err(ERROR_INVALID_NETWORK, data=data)
52
+ return Ok(Address(**res.json))
53
+ except Exception as err:
54
+ result = Err(err, data=data)
55
+ return result
56
+
57
+
58
+ def get_confirmed_balance(
59
+ address: str, testnet: bool = False, timeout: int = 10, proxies: Proxy = None, attempts: int = 1
60
+ ) -> Result[int]:
61
+ return get_address(address, testnet=testnet, timeout=timeout, proxies=proxies, attempts=attempts).and_then(
62
+ lambda a: Ok(a.chain_stats.funded_txo_sum - a.chain_stats.spent_txo_sum),
63
+ )
mm_btc/py.typed ADDED
File without changes
mm_btc/wallet.py ADDED
@@ -0,0 +1,45 @@
1
+ from dataclasses import dataclass
2
+
3
+ from hdwallet import BIP44HDWallet, BIP84HDWallet
4
+ from hdwallet.symbols import BTC, BTCTEST
5
+ from hdwallet.utils import generate_mnemonic as new_mnemonic
6
+
7
+ BIP44_MAINNET_PATH = "m/44'/0'/0'/0"
8
+ BIP44_TESTNET_PATH = "m/44'/1'/0'/0"
9
+ BIP84_MAINNET_PATH = "m/84'/0'/0'/0"
10
+ BIP84_TESTNET_PATH = "m/84'/1'/0'/0"
11
+
12
+
13
+ @dataclass
14
+ class Account:
15
+ address: str
16
+ private: str
17
+ path: str
18
+
19
+
20
+ def generate_mnemonic(language: str = "english", strength: int = 128) -> str:
21
+ return new_mnemonic(language=language, strength=strength) # type: ignore[no-any-return]
22
+
23
+
24
+ def derive_accounts(mnemonic: str, passphrase: str, path: str, limit: int) -> list[Account]:
25
+ if path.startswith("m/84'/1'"):
26
+ w = BIP84HDWallet(symbol=BTCTEST)
27
+ elif path.startswith("m/44'/1'"):
28
+ w = BIP44HDWallet(symbol=BTCTEST)
29
+ elif path.startswith("m/84'/0'"):
30
+ w = BIP84HDWallet(symbol=BTC)
31
+ elif path.startswith("m/44'/0'"):
32
+ w = BIP44HDWallet(symbol=BTC)
33
+ else:
34
+ raise ValueError("Invalid path")
35
+
36
+ w.from_mnemonic(mnemonic, passphrase=passphrase)
37
+ w.clean_derivation()
38
+
39
+ accounts = []
40
+ for index_path in range(limit):
41
+ w.from_path(path=f"{path}/{index_path}")
42
+ accounts.append(Account(address=w.address(), private=w.wif(), path=f"{path}/{index_path}"))
43
+ w.clean_derivation()
44
+
45
+ return accounts
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.1
2
+ Name: mm-btc
3
+ Version: 0.0.2
4
+ Requires-Python: >=3.12
5
+ Requires-Dist: mm-std ~=0.1.0
6
+ Requires-Dist: hdwallet ~=2.2.1
7
+ Provides-Extra: dev
8
+ Requires-Dist: build ~=1.2.1 ; extra == 'dev'
9
+ Requires-Dist: twine ~=5.1.0 ; extra == 'dev'
10
+ Requires-Dist: pytest ~=8.3.2 ; extra == 'dev'
11
+ Requires-Dist: pytest-xdist ~=3.6.1 ; extra == 'dev'
12
+ Requires-Dist: pytest-httpserver ~=1.0.8 ; extra == 'dev'
13
+ Requires-Dist: coverage ~=7.6.0 ; extra == 'dev'
14
+ Requires-Dist: ruff ~=0.5.2 ; extra == 'dev'
15
+ Requires-Dist: pip-audit ~=2.7.0 ; extra == 'dev'
16
+ Requires-Dist: bandit ~=1.7.7 ; extra == 'dev'
17
+ Requires-Dist: mypy ~=1.11.0 ; extra == 'dev'
18
+ Requires-Dist: types-python-dateutil ~=2.9.0 ; extra == 'dev'
19
+ Requires-Dist: types-requests ~=2.32.0.20240523 ; extra == 'dev'
20
+ Requires-Dist: types-PyYAML ~=6.0.12.12 ; extra == 'dev'
21
+
@@ -0,0 +1,8 @@
1
+ mm_btc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ mm_btc/blockstream.py,sha256=HllRQBO1Ezl0ArVH8b-CPRi0f5zdRNXJT_K3oCpAtgg,2159
3
+ mm_btc/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ mm_btc/wallet.py,sha256=XI2Ju5dELc_2wqyag6M-k5s3EZODN9qJKm3yjurJORE,1375
5
+ mm_btc-0.0.2.dist-info/METADATA,sha256=LdFWJbmxkPxaaxnInxW9KlEDh_QMji8xMbf4-n8YxtM,828
6
+ mm_btc-0.0.2.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
7
+ mm_btc-0.0.2.dist-info/top_level.txt,sha256=0X0Ppv_QY0ulzgCB5HNezpaHfQ4wvih_B72hn5hHTUE,7
8
+ mm_btc-0.0.2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (72.1.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ mm_btc