mm-btc 0.4.2__tar.gz → 0.5.1__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.5.1/PKG-INFO +11 -0
- mm_btc-0.5.1/README.md +154 -0
- {mm_btc-0.4.2 → mm_btc-0.5.1}/pyproject.toml +34 -27
- {mm_btc-0.4.2 → mm_btc-0.5.1}/src/mm_btc/blockstream.py +12 -10
- {mm_btc-0.4.2 → mm_btc-0.5.1}/src/mm_btc/cli/cli.py +2 -2
- {mm_btc-0.4.2 → mm_btc-0.5.1}/src/mm_btc/cli/cmd/address_cmd.py +2 -2
- {mm_btc-0.4.2 → mm_btc-0.5.1}/src/mm_btc/cli/cmd/create_tx_cmd.py +3 -2
- {mm_btc-0.4.2 → mm_btc-0.5.1}/src/mm_btc/cli/cmd/decode_tx_cmd.py +2 -2
- {mm_btc-0.4.2 → mm_btc-0.5.1}/src/mm_btc/cli/cmd/mnemonic_cmd.py +4 -4
- {mm_btc-0.4.2 → mm_btc-0.5.1}/src/mm_btc/cli/cmd/utxo_cmd.py +2 -2
- {mm_btc-0.4.2 → mm_btc-0.5.1}/tests/conftest.py +9 -3
- {mm_btc-0.4.2 → mm_btc-0.5.1}/tests/test_blockstream.py +2 -2
- mm_btc-0.5.1/uv.lock +1371 -0
- mm_btc-0.4.2/PKG-INFO +0 -10
- mm_btc-0.4.2/uv.lock +0 -1594
- {mm_btc-0.4.2 → mm_btc-0.5.1}/.env.example +0 -0
- {mm_btc-0.4.2 → mm_btc-0.5.1}/.gitignore +0 -0
- {mm_btc-0.4.2 → mm_btc-0.5.1}/README.txt +0 -0
- {mm_btc-0.4.2 → mm_btc-0.5.1}/dict.dic +0 -0
- {mm_btc-0.4.2 → mm_btc-0.5.1}/justfile +0 -0
- {mm_btc-0.4.2 → mm_btc-0.5.1}/src/mm_btc/__init__.py +0 -0
- {mm_btc-0.4.2 → mm_btc-0.5.1}/src/mm_btc/cli/__init__.py +0 -0
- {mm_btc-0.4.2 → mm_btc-0.5.1}/src/mm_btc/cli/cmd/__init__.py +0 -0
- {mm_btc-0.4.2 → mm_btc-0.5.1}/src/mm_btc/py.typed +0 -0
- {mm_btc-0.4.2 → mm_btc-0.5.1}/src/mm_btc/tx.py +0 -0
- {mm_btc-0.4.2 → mm_btc-0.5.1}/src/mm_btc/wallet.py +0 -0
- {mm_btc-0.4.2 → mm_btc-0.5.1}/tests/__init__.py +0 -0
- {mm_btc-0.4.2 → mm_btc-0.5.1}/tests/cmd/__init__.py +0 -0
- {mm_btc-0.4.2 → mm_btc-0.5.1}/tests/cmd/test_mnemonic_cmd.py +0 -0
- {mm_btc-0.4.2 → mm_btc-0.5.1}/tests/test_wallet.py +0 -0
mm_btc-0.5.1/PKG-INFO
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mm-btc
|
|
3
|
+
Version: 0.5.1
|
|
4
|
+
Requires-Python: >=3.13
|
|
5
|
+
Requires-Dist: bitcoinlib~=0.7.4
|
|
6
|
+
Requires-Dist: bit~=0.8.0
|
|
7
|
+
Requires-Dist: hdwallet~=3.4.0
|
|
8
|
+
Requires-Dist: mm-cryptocurrency~=0.4.2
|
|
9
|
+
Requires-Dist: mm-print>=0.0.3
|
|
10
|
+
Requires-Dist: mnemonic~=0.21
|
|
11
|
+
Requires-Dist: typer~=0.16.0
|
mm_btc-0.5.1/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# mm-btc
|
|
2
|
+
|
|
3
|
+
A Python library and CLI tool for Bitcoin operations, designed for developers who need to work with Bitcoin wallets, transactions, and blockchain data.
|
|
4
|
+
|
|
5
|
+
⚠️ **This project is under active development. New features and breaking changes may be introduced.**
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Library Usage
|
|
11
|
+
|
|
12
|
+
### Working with Mnemonics and Wallets
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from mm_btc.wallet import generate_mnemonic, derive_accounts, AddressType
|
|
16
|
+
|
|
17
|
+
# Generate a new mnemonic
|
|
18
|
+
mnemonic = generate_mnemonic(words=12)
|
|
19
|
+
print(f"Mnemonic: {mnemonic}")
|
|
20
|
+
|
|
21
|
+
# Derive accounts from mnemonic
|
|
22
|
+
accounts = derive_accounts(
|
|
23
|
+
mnemonic=mnemonic,
|
|
24
|
+
passphrase="",
|
|
25
|
+
path_prefix="m/84'/0'/0'/0", # BIP84 mainnet
|
|
26
|
+
address_type=AddressType.P2WPKH,
|
|
27
|
+
limit=5
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
for account in accounts:
|
|
31
|
+
print(f"Address: {account.address}")
|
|
32
|
+
print(f"Private Key: {account.private}")
|
|
33
|
+
print(f"WIF: {account.wif}")
|
|
34
|
+
print(f"Path: {account.path}")
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Blockchain API Integration
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import asyncio
|
|
41
|
+
from mm_btc.blockstream import BlockstreamClient
|
|
42
|
+
|
|
43
|
+
async def get_address_info():
|
|
44
|
+
client = BlockstreamClient(testnet=False)
|
|
45
|
+
|
|
46
|
+
# Get address information
|
|
47
|
+
address_info = await client.get_address("bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh")
|
|
48
|
+
if address_info.is_ok():
|
|
49
|
+
print(f"Balance: {address_info.value.chain_stats.funded_txo_sum}")
|
|
50
|
+
|
|
51
|
+
# Get UTXOs
|
|
52
|
+
utxos = await client.get_utxo("bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh")
|
|
53
|
+
if utxos.is_ok():
|
|
54
|
+
for utxo in utxos.value:
|
|
55
|
+
print(f"TXID: {utxo.txid}, Value: {utxo.value}")
|
|
56
|
+
|
|
57
|
+
asyncio.run(get_address_info())
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## CLI Usage
|
|
62
|
+
|
|
63
|
+
The `mm-btc` command provides several subcommands for Bitcoin operations.
|
|
64
|
+
|
|
65
|
+
### Mnemonic Operations
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Generate a new 12-word mnemonic and derive addresses
|
|
69
|
+
mm-btc mnemonic
|
|
70
|
+
|
|
71
|
+
# Use existing mnemonic with custom parameters
|
|
72
|
+
mm-btc mnemonic -m "your twelve word mnemonic phrase here..." -l 5 --address-type P2WPKH
|
|
73
|
+
|
|
74
|
+
# Generate testnet addresses
|
|
75
|
+
mm-btc mnemonic --testnet --path bip84
|
|
76
|
+
|
|
77
|
+
# Use custom derivation path
|
|
78
|
+
mm-btc mnemonic --path "m/44'/0'/0'/0" --hex
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Address Information
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Get address information from Blockstream API
|
|
85
|
+
mm-btc address bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh
|
|
86
|
+
|
|
87
|
+
# Get UTXOs for an address
|
|
88
|
+
mm-btc utxo bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Transaction Operations
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# Create a transaction from config file
|
|
95
|
+
mm-btc create-tx config.toml
|
|
96
|
+
|
|
97
|
+
# Decode a transaction
|
|
98
|
+
mm-btc decode-tx 02000000000101... --testnet
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Configuration Example
|
|
102
|
+
|
|
103
|
+
Create a `config.toml` file for transaction creation:
|
|
104
|
+
|
|
105
|
+
```toml
|
|
106
|
+
from_address = "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh"
|
|
107
|
+
private = "your_private_key_wif_format"
|
|
108
|
+
|
|
109
|
+
[[outputs]]
|
|
110
|
+
address = "bc1qrecipient_address_here"
|
|
111
|
+
amount = 50000 # Amount in satoshis
|
|
112
|
+
|
|
113
|
+
[[outputs]]
|
|
114
|
+
address = "bc1qanother_recipient"
|
|
115
|
+
amount = 25000
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Address Types
|
|
119
|
+
|
|
120
|
+
Supported Bitcoin address types:
|
|
121
|
+
|
|
122
|
+
- `P2PKH` - Pay to Public Key Hash (Legacy)
|
|
123
|
+
- `P2SH` - Pay to Script Hash
|
|
124
|
+
- `P2WPKH` - Native SegWit (Bech32)
|
|
125
|
+
- `P2WPKH_IN_P2SH` - SegWit wrapped in P2SH
|
|
126
|
+
- `P2WSH` - Native SegWit Script
|
|
127
|
+
- `P2WSH_IN_P2SH` - SegWit Script wrapped in P2SH
|
|
128
|
+
- `P2TR` - Taproot
|
|
129
|
+
|
|
130
|
+
## Derivation Paths
|
|
131
|
+
|
|
132
|
+
- **BIP44**: `m/44'/0'/0'/0` (mainnet) / `m/44'/1'/0'/0` (testnet)
|
|
133
|
+
- **BIP84**: `m/84'/0'/0'/0` (mainnet) / `m/84'/1'/0'/0` (testnet)
|
|
134
|
+
- **Custom**: Specify your own path like `m/44'/0'/0'/0`
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
## Dependencies
|
|
138
|
+
|
|
139
|
+
- `hdwallet` - HD wallet operations
|
|
140
|
+
- `bit` - Bitcoin transaction creation
|
|
141
|
+
- `bitcoinlib` - Bitcoin utilities
|
|
142
|
+
- `mnemonic` - BIP39 mnemonic generation
|
|
143
|
+
- `typer` - CLI framework
|
|
144
|
+
- `mm-print` - Pretty printing utilities
|
|
145
|
+
- `mm-cryptocurrency` - Cryptocurrency utilities
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
## References
|
|
149
|
+
|
|
150
|
+
- [Bitcoin Transactions](https://en.bitcoin.it/wiki/Transaction)
|
|
151
|
+
- [Address Prefixes](https://en.bitcoin.it/wiki/List_of_address_prefixes)
|
|
152
|
+
- [Bitcoin BIPs](https://github.com/bitcoin/bips/blob/master/README.mediawiki)
|
|
153
|
+
- [Blockstream API](https://github.com/Blockstream/esplora/blob/master/API.md)
|
|
154
|
+
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mm-btc"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.5.1"
|
|
4
4
|
description = ""
|
|
5
|
-
requires-python = ">=3.
|
|
5
|
+
requires-python = ">=3.13"
|
|
6
6
|
dependencies = [
|
|
7
|
-
"mm-crypto-utils>=0.3.6",
|
|
8
7
|
"hdwallet~=3.4.0",
|
|
9
8
|
"bit~=0.8.0",
|
|
10
|
-
"bitcoinlib~=0.7.
|
|
9
|
+
"bitcoinlib~=0.7.4",
|
|
11
10
|
"mnemonic~=0.21",
|
|
12
|
-
"typer~=0.
|
|
11
|
+
"typer~=0.16.0",
|
|
12
|
+
"mm-print>=0.0.3",
|
|
13
|
+
"mm-cryptocurrency~=0.4.2",
|
|
13
14
|
]
|
|
14
15
|
[project.scripts]
|
|
15
16
|
mm-btc = "mm_btc.cli.cli:app"
|
|
@@ -21,14 +22,15 @@ build-backend = "hatchling.build"
|
|
|
21
22
|
|
|
22
23
|
[tool.uv]
|
|
23
24
|
dev-dependencies = [
|
|
24
|
-
"pytest~=8.
|
|
25
|
-
"pytest-
|
|
25
|
+
"pytest~=8.4.0",
|
|
26
|
+
"pytest-asyncio~=1.0.0",
|
|
27
|
+
"pytest-xdist~=3.7.0",
|
|
26
28
|
"pytest-httpserver~=1.1.3",
|
|
27
|
-
"ruff~=0.11.
|
|
29
|
+
"ruff~=0.11.13",
|
|
28
30
|
"pip-audit~=2.9.0",
|
|
29
31
|
"bandit~=1.8.3",
|
|
30
|
-
"mypy~=1.
|
|
31
|
-
"
|
|
32
|
+
"mypy~=1.16.0",
|
|
33
|
+
"python-dotenv>=1.1.0",
|
|
32
34
|
]
|
|
33
35
|
|
|
34
36
|
|
|
@@ -38,7 +40,12 @@ warn_no_return = false
|
|
|
38
40
|
strict = true
|
|
39
41
|
exclude = ["^tests/", "^tmp/"]
|
|
40
42
|
[[tool.mypy.overrides]]
|
|
41
|
-
module = [
|
|
43
|
+
module = [
|
|
44
|
+
"hdwallet.*",
|
|
45
|
+
"hdwallet.symbols.*",
|
|
46
|
+
"bit.*",
|
|
47
|
+
"bitcoinlib.transactions.*",
|
|
48
|
+
]
|
|
42
49
|
ignore_missing_imports = true
|
|
43
50
|
|
|
44
51
|
[tool.ruff]
|
|
@@ -47,27 +54,27 @@ target-version = "py313"
|
|
|
47
54
|
[tool.ruff.lint]
|
|
48
55
|
select = ["ALL"]
|
|
49
56
|
ignore = [
|
|
50
|
-
"TC",
|
|
51
|
-
"A005",
|
|
52
|
-
"ERA001",
|
|
53
|
-
"PT",
|
|
54
|
-
"D",
|
|
55
|
-
"FIX",
|
|
57
|
+
"TC", # flake8-type-checking, TYPE_CHECKING is dangerous, for example it doesn't work with pydantic
|
|
58
|
+
"A005", # flake8-builtins: stdlib-module-shadowing
|
|
59
|
+
"ERA001", # eradicate: commented-out-code
|
|
60
|
+
"PT", # flake8-pytest-style
|
|
61
|
+
"D", # pydocstyle
|
|
62
|
+
"FIX", # flake8-fixme
|
|
56
63
|
"PLR0911", # pylint: too-many-return-statements
|
|
57
64
|
"PLR0912", # pylint: too-many-branches
|
|
58
65
|
"PLR0913", # pylint: too-many-arguments
|
|
59
66
|
"PLR2004", # pylint: magic-value-comparison
|
|
60
67
|
"PLC0414", # pylint: useless-import-alias
|
|
61
|
-
"FBT",
|
|
62
|
-
"EM",
|
|
63
|
-
"TRY003",
|
|
64
|
-
"C901",
|
|
65
|
-
"BLE001",
|
|
66
|
-
"S311",
|
|
67
|
-
"TD002",
|
|
68
|
-
"TD003",
|
|
69
|
-
"RET503",
|
|
70
|
-
"COM812",
|
|
68
|
+
"FBT", # flake8-boolean-trap
|
|
69
|
+
"EM", # flake8-errmsg
|
|
70
|
+
"TRY003", # tryceratops: raise-vanilla-args
|
|
71
|
+
"C901", # mccabe: complex-structure,
|
|
72
|
+
"BLE001", # flake8-blind-except
|
|
73
|
+
"S311", # bandit: suspicious-non-cryptographic-random-usage
|
|
74
|
+
"TD002", # flake8-todos: missing-todo-author
|
|
75
|
+
"TD003", # flake8-todos: missing-todo-link
|
|
76
|
+
"RET503", # flake8-return: implicit-return
|
|
77
|
+
"COM812", # it's used in ruff formatter
|
|
71
78
|
]
|
|
72
79
|
[tool.ruff.lint.pep8-naming]
|
|
73
80
|
classmethod-decorators = ["field_validator"]
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from collections.abc import Sequence
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from mm_cryptocurrency import random_proxy
|
|
4
|
+
from mm_http import HttpResponse, http_request
|
|
5
|
+
from mm_result import Result
|
|
4
6
|
from pydantic import BaseModel
|
|
5
7
|
|
|
6
8
|
MAINNET_BASE_URL = "https://blockstream.info/api"
|
|
@@ -67,14 +69,14 @@ class BlockstreamClient:
|
|
|
67
69
|
res = await self._request(f"/address/{address}")
|
|
68
70
|
try:
|
|
69
71
|
if res.status_code == 400:
|
|
70
|
-
return res.
|
|
71
|
-
return res.
|
|
72
|
+
return res.to_result_err("400 Bad Request")
|
|
73
|
+
return res.to_result_ok(Address(**res.parse_json_body()))
|
|
72
74
|
except Exception as e:
|
|
73
|
-
result = res.
|
|
75
|
+
result = res.to_result_err(e)
|
|
74
76
|
return result
|
|
75
77
|
|
|
76
78
|
async def get_confirmed_balance(self, address: str) -> Result[int]:
|
|
77
|
-
return (await self.get_address(address)).
|
|
79
|
+
return (await self.get_address(address)).chain(
|
|
78
80
|
lambda a: Result.ok(a.chain_stats.funded_txo_sum - a.chain_stats.spent_txo_sum)
|
|
79
81
|
)
|
|
80
82
|
|
|
@@ -83,9 +85,9 @@ class BlockstreamClient:
|
|
|
83
85
|
for _ in range(self.attempts):
|
|
84
86
|
res = await self._request(f"/address/{address}/utxo")
|
|
85
87
|
try:
|
|
86
|
-
return res.
|
|
88
|
+
return res.to_result_ok([Utxo(**out) for out in res.parse_json_body()])
|
|
87
89
|
except Exception as e:
|
|
88
|
-
result = res.
|
|
90
|
+
result = res.to_result_err(e)
|
|
89
91
|
return result
|
|
90
92
|
|
|
91
93
|
async def get_mempool(self) -> Result[Mempool]:
|
|
@@ -93,10 +95,10 @@ class BlockstreamClient:
|
|
|
93
95
|
for _ in range(self.attempts):
|
|
94
96
|
res = await self._request("/mempool")
|
|
95
97
|
try:
|
|
96
|
-
return res.
|
|
98
|
+
return res.to_result_ok(Mempool(**res.parse_json_body()))
|
|
97
99
|
except Exception as e:
|
|
98
|
-
result = res.
|
|
100
|
+
result = res.to_result_err(e)
|
|
99
101
|
return result
|
|
100
102
|
|
|
101
103
|
async def _request(self, url: str) -> HttpResponse:
|
|
102
|
-
return await http_request(f"{self.base_url}{url}", timeout=self.timeout, proxy=
|
|
104
|
+
return await http_request(f"{self.base_url}{url}", timeout=self.timeout, proxy=random_proxy(self.proxies))
|
|
@@ -3,9 +3,9 @@ import importlib.metadata
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Annotated
|
|
5
5
|
|
|
6
|
+
import mm_print
|
|
6
7
|
import typer
|
|
7
8
|
import typer.core
|
|
8
|
-
from mm_std import print_plain
|
|
9
9
|
|
|
10
10
|
from mm_btc.wallet import AddressType
|
|
11
11
|
|
|
@@ -68,7 +68,7 @@ def utxo_command(address: str) -> None:
|
|
|
68
68
|
|
|
69
69
|
def version_callback(value: bool) -> None:
|
|
70
70
|
if value:
|
|
71
|
-
print_plain(f"mm-btc: v{importlib.metadata.version('mm-btc')}")
|
|
71
|
+
mm_print.print_plain(f"mm-btc: v{importlib.metadata.version('mm-btc')}")
|
|
72
72
|
raise typer.Exit
|
|
73
73
|
|
|
74
74
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import mm_print
|
|
2
2
|
|
|
3
3
|
from mm_btc.blockstream import BlockstreamClient
|
|
4
4
|
from mm_btc.wallet import is_testnet_address
|
|
@@ -7,4 +7,4 @@ from mm_btc.wallet import is_testnet_address
|
|
|
7
7
|
async def run(address: str) -> None:
|
|
8
8
|
client = BlockstreamClient(testnet=is_testnet_address(address))
|
|
9
9
|
res = await client.get_address(address)
|
|
10
|
-
print_json(res.value_or_error())
|
|
10
|
+
mm_print.print_json(res.value_or_error())
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
|
|
3
|
+
import mm_print
|
|
3
4
|
from bit import PrivateKey, PrivateKeyTestnet
|
|
4
|
-
from
|
|
5
|
+
from mm_cryptocurrency import BaseConfig
|
|
5
6
|
|
|
6
7
|
from mm_btc.wallet import is_testnet_address
|
|
7
8
|
|
|
@@ -24,4 +25,4 @@ def run(config_path: Path) -> None:
|
|
|
24
25
|
outputs = [(o.address, o.amount, "satoshi") for o in config.outputs]
|
|
25
26
|
|
|
26
27
|
tx = key.create_transaction(outputs)
|
|
27
|
-
|
|
28
|
+
mm_print.print_json(tx)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from enum import Enum
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
import mm_print
|
|
5
5
|
|
|
6
6
|
from mm_btc.wallet import AddressType, derive_accounts, generate_mnemonic
|
|
7
7
|
|
|
@@ -29,12 +29,12 @@ def run(args: Args) -> None:
|
|
|
29
29
|
path = get_derivation_path_prefix(args.path, args.testnet)
|
|
30
30
|
accounts = derive_accounts(mnemonic, passphrase, path, args.address_type, args.limit)
|
|
31
31
|
|
|
32
|
-
print_plain(f"{mnemonic}")
|
|
32
|
+
mm_print.print_plain(f"{mnemonic}")
|
|
33
33
|
if passphrase:
|
|
34
|
-
print_plain(f"{passphrase}")
|
|
34
|
+
mm_print.print_plain(f"{passphrase}")
|
|
35
35
|
for acc in accounts:
|
|
36
36
|
private = acc.private if args.hex else acc.wif
|
|
37
|
-
print_plain(f"{acc.path} {acc.address} {private}")
|
|
37
|
+
mm_print.print_plain(f"{acc.path} {acc.address} {private}")
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
def get_derivation_path_prefix(path: str, testnet: bool) -> str:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import mm_print
|
|
2
2
|
|
|
3
3
|
from mm_btc.blockstream import BlockstreamClient
|
|
4
4
|
from mm_btc.wallet import is_testnet_address
|
|
@@ -7,4 +7,4 @@ from mm_btc.wallet import is_testnet_address
|
|
|
7
7
|
async def run(address: str) -> None:
|
|
8
8
|
client = BlockstreamClient(testnet=is_testnet_address(address))
|
|
9
9
|
res = await client.get_utxo(address)
|
|
10
|
-
print_json(res.value_or_error())
|
|
10
|
+
mm_print.print_json(res.value_or_error())
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
1
3
|
import pytest
|
|
2
|
-
from
|
|
3
|
-
from
|
|
4
|
+
from dotenv import load_dotenv
|
|
5
|
+
from mm_cryptocurrency import fetch_proxies_sync
|
|
4
6
|
from typer.testing import CliRunner
|
|
5
7
|
|
|
8
|
+
load_dotenv()
|
|
9
|
+
|
|
10
|
+
PROXIES_URL = os.getenv("PROXIES_URL")
|
|
11
|
+
|
|
6
12
|
|
|
7
13
|
@pytest.fixture()
|
|
8
14
|
def mnemonic() -> str:
|
|
@@ -26,7 +32,7 @@ def binance_address() -> str:
|
|
|
26
32
|
|
|
27
33
|
@pytest.fixture
|
|
28
34
|
def proxies() -> list[str]:
|
|
29
|
-
return
|
|
35
|
+
return fetch_proxies_sync(PROXIES_URL).unwrap("cannot fetch proxies")
|
|
30
36
|
|
|
31
37
|
|
|
32
38
|
@pytest.fixture
|
|
@@ -19,11 +19,11 @@ async def test_get_address(binance_address, proxies):
|
|
|
19
19
|
|
|
20
20
|
# invalid address
|
|
21
21
|
res = await client.get_address("bc1pa48c294qk7yd7sc8y0wxydc3a2frv5j83e65rvm48v3ej098s5zs8kvh5d")
|
|
22
|
-
assert res.
|
|
22
|
+
assert res.unwrap_err() == blockstream.ERROR_400_BAD_REQUEST
|
|
23
23
|
|
|
24
24
|
# invalid network
|
|
25
25
|
res = await client.get_address("mqkwWDWdgXhYunfoKvQfYyydwB5vdma3cK")
|
|
26
|
-
assert res.
|
|
26
|
+
assert res.unwrap_err() == blockstream.ERROR_400_BAD_REQUEST
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
async def test_get_confirmed_balance(binance_address, proxies):
|