Drop everything that duplicates PyCardano. The landscape scan done
2026-04-23 confirmed: no ecosystem gap for wallet/chain/tx-build —
pycardano 0.19.x covers all of it cleanly. The gap is the merchant
state machine, so that's all we ship.
Deleted:
- addresses.py → consumers call pycardano.HDWallet directly
- txbuild.py → consumers use pycardano.OgmiosChainContext directly
- oracles.py → consumers supply a price_fn callable
- mint.py → consumers build mint txs with pycardano;
CIP-25 v2 metadata builder shipped as a copy-paste
snippet in the README
- ipfs.py → py-ipfs-http-client covers it
- tradecraft_compat.py → no one was importing it; kill
- docs/minting-workflow.md → redundant with README pairing guidance
Refactored:
- monitor.evaluate_utxos: ADA-only. The DexHunter token-equivalent
block came out. Consumers who want stablecoin support wrap the
function with their own asset-to-lovelace converter.
- monitor.reprice_expired_invoices: takes a new required kwarg
price_fn: Callable[[float], Awaitable[int]]. No more ADA/USD
oracle shipped in the SDK.
- scheduler.InvoiceScheduler: takes an optional price_fn field;
if None, the reprice job is a no-op (works for fixed-ADA invoices).
Tests (26/26 passing, all offline):
- test_invoice.py — state-machine helpers, 3 tests
- test_store_protocol.py — Protocol conformance + InMemoryStore round-trips, 13 tests
- test_monitor_with_inmemory_store.py — all status transitions + reprice,
rewired to pass price_fn fixtures instead of monkeypatching oracle funcs
Deps dropped: pycardano (consumer pairing, not our dep).
Deps kept: httpx (Koios), apscheduler (background scheduler).
Package shape (1.0.0-dev, ~700 LOC src):
cardano_checkout/
invoice.py — Invoice + InvoiceStatus
store.py — InvoiceStore Protocol + InMemoryStore
monitor.py — Koios poll + UTxO matching + reprice driver
scheduler.py — APScheduler wrapper
README rewritten top-to-bottom: "what we ship", "what we don't ship",
why the niche exists, pycardano-directly examples for the delete-list,
CIP-25 builder as a 20-line copy-paste, InvoiceStore implementation
example. Apache-2.0 license unchanged.
txbuild.py gets real: make_ogmios_context builds an
OgmiosChainContext pointed at 127.0.0.1:1337 by default (matches the
Rackham stack), get_protocol_parameters peeks at live params,
get_address_utxos powers the eventual refund path, submit_signed_tx
ships a cold-signed blob to the chain.
mint.py's mint_nft_cert now constructs the real tx body:
- mints exactly 1 of {policy_id}.{asset_name}
- sends it to recipient_address in its own UTxO padded with min-ADA
- attaches CIP-25 v2 metadata + the native script as aux data
- clamps TTL to policy.locked_after_slot when the policy is time-locked
Does NOT sign — matches the ADAMaps cold-signing pattern. Returns an
UnsignedMint bundle carrying body CBOR, aux CBOR, native-script CBOR,
required-signer hashes, and a human-readable summary. Operator moves
the bundle to the cold host (Lucy in Sulkta's pattern — 2-of-2
native-script under Cobb + Kayos skeys), signs offline, ships the
signed CBOR back. submit_signed_tx finishes the round-trip.
Rewrite monitor.py so it operates entirely through the
cardano_checkout.store.InvoiceStore Protocol — no more SQLAlchemy
imports, no more CardanoPayment / PlatformConfig coupling. Same
behavioural shape: same Koios URL, same 15s check cadence, same 2%
confirm / overpay tolerances, same 3-reprice cap.
Rewrite scheduler.py as a reusable InvoiceScheduler dataclass that
wires two APScheduler jobs (check_pending every 15s, reprice_expired
every 60s) against a consumer-supplied store. The subscription +
grace-period jobs are TradeCraft-specific and get lifted into
tradecraft_compat.py verbatim so TradeCraft can still import them
during the migration window without any code change.
Add InMemoryStore reference implementation to store.py — used by the
test suite and handy for local dev / ephemeral workflows.
Bump version to 0.2.0-dev.
pycardano >=0.11 doesn't ship HDPublicKey — the v0.1 extraction
imported a symbol that never existed in the installed version, which
meant every address-derivation test failed at import time.
Use HDWallet rooted at the account level with public-only fields set,
then soft-derive receive chain (0) and staking chain (2). Hash the
resulting verification keys through PaymentVerificationKey /
StakeVerificationKey to compose the Shelley base address.
Refresh the test-vector xpub with a real, deterministic CIP-1852
account xpub derived from the well-known "test ... junk" mnemonic so
validate_xpub + derive_address actually exercise the BIP32 math.
Drop the "random hex should be rejected" assertion — BIP32-ED25519
soft derivation doesn't enforce that the 32-byte pubkey half is a
point on the curve, so arbitrary well-shaped hex is accepted by the
underlying crypto.