API reference
Every method exists on both Client (sync) and AsyncClient (async)
with identical names, parameters, and return shapes. The async version
is async def and must be awaited; otherwise they're interchangeable.
Amounts are integers in exfers, where 1 EXFER = 100_000_000 exfers.
Hex strings (addresses, hashes, scripts, tx bytes) are lowercase, no
0x prefix.
Construction
Client(url: str, token: str, *, timeout: float = 30.0,
transport: httpx.BaseTransport | None = None,
fingerprint: str | None = None)
AsyncClient(url, token, *, timeout=30.0, transport=None, fingerprint=None)
fingerprint enables TLS pinning when walletd is run with --tls.
Format is "sha256:<lowercase-hex-64>" (the exact string walletd
writes to <datadir>/cert.fingerprint on first run). Requires an
https:// URL. The pinning transport replaces CA-chain validation —
walletd's leaf cert is trusted iff its SHA-256 matches.
transport= and fingerprint= are mutually exclusive — the latter
installs a pinning transport itself, and accepting a custom one
alongside would silently bypass verification.
Alternate constructors:
Client.from_env(*, url_env="WALLETD_URL",
token_env="WALLETD_AUTH_TOKEN",
fingerprint_env="WALLETD_FINGERPRINT")
Client.from_datadir(*, url="http://127.0.0.1:7448",
datadir="~/.exfer-walletd")
from_env reads WALLETD_FINGERPRINT if set (otherwise plaintext
HTTP). from_datadir auto-reads <datadir>/cert.fingerprint when
url is https://, raising FileNotFoundError if walletd hasn't
been started with --tls yet.
Raise RuntimeError (from_env) or FileNotFoundError
(from_datadir) if the required inputs are missing.
Liveness
healthz() -> bool
Probe GET /healthz — TCP+HTTP only. Returns True iff walletd
answered 200 OK with body ok. Returns False on any failure
rather than raising — drops cleanly into liveness loops.
No Authorization header is sent. A green healthz says nothing
about whether your token is valid or whether walletd's upstream node
is reachable.
ping() -> None
Authenticated JSON-RPC round-trip. Returns None on success and
raises on any failure. Use this when you want to verify the token is
valid and walletd's RPC layer is up — not just the TCP socket.
Read-scope methods
generate_address() -> str
Create a new managed address. Returns the address (lowercase 64-char
hex). Walletd persists the keypair on disk under
<datadir>/wallets/<address>.key.
list_addresses() -> list[str]
Every address walletd holds a key for, sorted ascending.
get_balance(address: str) -> int
Confirmed balance for address, in exfers. Mempool UTXOs are not
counted; for the mempool-aware view, use get_address_utxos.
get_address_utxos(address: str) -> UtxosResult
Confirmed UTXOs locked to address plus tip metadata:
{
"address": "27e1c8...",
"script_hex": None,
"tip_height": 577429,
"truncated": False,
"utxos": [
{
"tx_id": "a02ab0...",
"output_index": 1,
"value": 69900000,
"height": 577429,
"is_coinbase": False,
"script_len": None,
},
],
}
truncated is True if the upstream hit a result limit.
get_script_utxos(script_hex: str) -> UtxosResult
Same shape as get_address_utxos, but matches by raw locking script.
address is always None in the result.
get_block_height() -> int
Current chain tip height. For the (height, block_id) pair, use
get_tip().
get_tip() -> Tip
Current chain tip as a NamedTuple:
from exfer_walletd import Tip
tip = c.get_tip()
print(tip.height, tip.block_id)
h, b = tip # unpack works too
get_block_by_height(height: int) -> Block
{
"hash": "17b95f...",
"height": 577429,
"prev_block_id": "...",
"state_root": "...",
"tx_root": "...",
"timestamp": 1700000000,
"nonce": 42,
"difficulty_target": "...",
"tx_count": 1,
"transactions": ["a02ab0..."],
}
get_block_by_hash(block_hash: str) -> Block
Same shape; lookup by block hash instead of height.
get_transaction(tx_id: str) -> Transaction
Fetch a transaction. Covers mempool + confirmed; in_mempool
distinguishes.
{
"tx_id": "a02ab0...",
"tx_hex": "01000200...",
"in_mempool": False,
"block_hash": "1bac70...", # None if in mempool
"block_height": 577429, # None if in mempool
}
Spend-scope methods
transfer(*, from_, to, amount, fee=None) -> TransferResult
Build, sign, and broadcast a payment from a managed wallet.
from_(trailing underscore) maps to wire fieldfrom.amount,feeare integers in exfers.- Omitting
feelets walletd apply its default (100_000 = 0.001 EXFER).
{
"tx_id": "a02ab0...",
"size": 227,
"tip_height": 577427,
"submitted": True,
}
Common errors:
WalletNotFoundError— walletd doesn't hold the key forfrom_.InsufficientBalanceError— check.in_flight_reservedto decide whether to retry.UpstreamError— node rejected the broadcast or is unreachable.TxAuthError— UTXO authentication failed; the upstream may be malicious or out of sync.
See Errors for the full list.
send_raw_transaction(tx_hex: str) -> str
Broadcast a pre-signed transaction. Returns the broadcast tx_id.
Used by transfer internally; exposed for callers that build
transactions externally.
Type definitions
Single-value methods return bare Python types (str, int).
Multi-field methods return TypedDicts or NamedTuples — import them
from exfer_walletd.types if you want to annotate variables.
from exfer_walletd.types import (
Block,
Transaction,
TransferResult,
Utxo,
UtxosResult,
)
from exfer_walletd import Tip # NamedTuple — also top-level
You don't need to import any of these for normal use — they're just return-type annotations.