pyrxd.swap — Same-chain partial-transaction swaps¶
Same-chain partial-transaction swaps for RXD and Glyph FTs.
A high-level, guard-railed offer/accept API over SIGHASH_SINGLE |
ANYONECANPAY signature-level atomicity — the “maker signs one input
committing to one output, taker completes and broadcasts” pattern.
Quick start:
from pyrxd.swap import create_offer, accept_offer, Asset, FundingInput
# Maker: give 1000 RXD-photons, want 50 units of FT `ref`.
offer = create_offer(
give_source_tx=maker_utxo_source_tx,
give_vout=0,
maker_key=maker_key,
receive=Asset(kind="ft", amount=50, ref=ft_ref),
maker_receive_pkh=maker_pkh,
)
payload = offer.to_dict() # send over any transport
# Taker: verify + complete + sign.
tx = accept_offer(
SwapOffer.from_dict(payload),
funding=[FundingInput(source_tx=taker_ft_source, vout=0, key=taker_key)],
taker_receive_pkh=taker_pkh,
taker_change_pkh=taker_pkh,
fee=500,
)
raw = tx.serialize().hex() # broadcast
This is distinct from pyrxd.gravity, which does cross-chain
atomic swaps gated by SPV proofs. Use this for same-chain RXD/token
trades; use Gravity when the two assets live on different chains.
The core (create_offer / accept_offer) is pure — no network. Use
the pyrxd.swap.resolve helpers to fetch the transactions an offer
references.
- class pyrxd.swap.Asset[source]¶
Bases:
objectOne side of a trade: plain RXD, or a Glyph fungible token.
amountis in photons. For an FT this is also the token-unit count (Radiant convention: 1 photon = 1 FT unit).refis the FT’s genesis/commit outpoint (the permanent token identity) and is required for — and only for —kind == "ft".- __init__(kind, amount, ref=None)¶
- class pyrxd.swap.FundingInput[source]¶
Bases:
objectA taker-owned UTXO used to fund the maker’s receive + fee (and/or to pay an FT the maker wants).
source_txis the taker’s own previous transaction, so its value/script are trusted (the taker controls it).keysigns it.- __init__(source_tx, vout, key)¶
- Parameters:
source_tx (Transaction)
vout (int)
key (PrivateKey)
- Return type:
None
- source_tx: Transaction¶
- key: PrivateKey¶
- class pyrxd.swap.SwapOffer[source]¶
Bases:
objectA maker’s signed partial transaction plus everything a taker needs to verify it.
Transport-agnostic.
partial_tx_hexholds the maker’s input (signedSINGLE|ANYONECANPAY) and output[0] (what the maker wants to receive).give_source_tx_hexis the full previous transaction that funds the maker’s input, so the taker can read the maker’s real given-asset value/script from the chain rather than trusting the declaredterms— and confirm it hashes to the input’s outpoint.- __init__(partial_tx_hex, give_source_tx_hex, give_vout, terms)¶
- class pyrxd.swap.SwapTerms[source]¶
Bases:
objectThe trade as the maker states it: maker gives
give, receivesreceive.From the taker’s seat this reads in reverse — the taker receives
giveand paysreceive. The terms are a human-readable cross-check; the maker’s signature on the partial tx is what actually enforces them (seepyrxd.swap.partial.accept_offer()).
- pyrxd.swap.accept_offer(offer, *, funding, taker_receive_pkh, taker_change_pkh, fee)[source]¶
Complete and sign a maker’s offer, returning a broadcast-ready transaction.
Safety, by construction:
The maker’s given asset is read from
offer.give_source_tx_hex(verified to hash to the maker input’s outpoint) — never from the declared terms — and reconciled againstoffer.terms.give.The maker’s receive output (output[0]) is read from the partial tx and reconciled against
offer.terms.receive.The maker’s signature is re-verified both before and after the taker completes the transaction, so tampered terms are rejected.
Token conservation is enforced per FT ref; RXD change goes to the taker. The taker receives the maker’s given asset in output[1].
feeis the absolute fee in photons; the taker funds it.- Parameters:
- Return type:
- pyrxd.swap.create_offer(*, give_source_tx, give_vout, maker_key, receive, maker_receive_pkh)[source]¶
Build a maker’s signed partial-swap offer.
The maker offers to spend
give_source_tx.outputs[give_vout](the given asset, owned bymaker_key) in exchange forreceivepaid tomaker_receive_pkhin output[0]. The given input is signedSINGLE|ANYONECANPAYso any taker can complete the swap.The whole given UTXO is spent (its full value flows to the taker); pre-split the UTXO beforehand to sell a partial amount.
- async pyrxd.swap.fetch_funding_input(client, *, txid, vout, key)[source]¶
Resolve one of the taker’s UTXOs into a
FundingInputforaccept_offer.- Parameters:
client (ElectrumXClient)
vout (int)
key (PrivateKey)
- Return type:
- async pyrxd.swap.fetch_transaction(client, txid)[source]¶
Fetch and parse a transaction, verifying it actually hashes to txid.
The server-honesty check (computed txid == requested txid) means a hostile or buggy server cannot substitute a different transaction.
- Parameters:
client (ElectrumXClient)
- Return type: