pyrxd.gravity — Cross-chain atomic swaps

pyrxd.gravity — Gravity protocol covenant transaction builders and orchestrator.

Phase 3a implements the Radiant-side covenant transaction builders that correspond to the JS prototype’s claim_tx.js, finalize_tx.js, and forfeit_tx.js.

Phase 3b adds the high-level GravityTrade orchestrator that wraps the full 4-step BTC↔RXD swap into a single async class.

Public surface

  • GravityOffer — all Maker-committed parameters for a covenant

  • ClaimResult — output of build_claim_tx()

  • FinalizeResult — output of build_finalize_tx()

  • ForfeitResult — output of build_forfeit_tx()

  • build_claim_tx — spend MakerOffer → create MakerClaimed UTXO

  • build_finalize_tx — spend MakerClaimed → release photons to Taker

  • build_forfeit_tx — Maker reclaims after claimDeadline

  • compute_p2sh_code_hash — derive the expectedClaimedCodeHash a covenant checks

  • GravityTrade — high-level async swap orchestrator (Phase 3b)

  • TradeConfig — tunable parameters for GravityTrade

  • ConfirmationStatus— BTC confirmation poll result

class pyrxd.gravity.ActiveOffer[source]

Bases: object

State of a live Gravity MakerOffer on Radiant.

Returned by GravityMakerSession.create_offer() and required by all subsequent lifecycle methods.

offer

The original GravityOffer covenant parameters.

Type:

pyrxd.gravity.types.GravityOffer

maker_offer_result

Raw tx details from build_maker_offer_tx.

Type:

pyrxd.gravity.types.MakerOfferResult

offer_txid

Radiant txid of the confirmed MakerOffer funding output.

Type:

str

offer_vout

Output index of the MakerOffer P2SH UTXO (always 0).

Type:

int

offer_photons

Photons locked in the MakerOffer P2SH output.

Type:

int

__init__(offer, maker_offer_result, offer_txid, offer_vout, offer_photons)
Parameters:
Return type:

None

offer: GravityOffer
maker_offer_result: MakerOfferResult
offer_txid: str
offer_vout: int
offer_photons: int
class pyrxd.gravity.ClaimResult[source]

Bases: object

ClaimResult(tx_hex: ‘str’, txid: ‘str’, tx_size: ‘int’, offer_p2sh: ‘str’, claimed_p2sh: ‘str’, fee_sats: ‘int’, output_photons: ‘int’)

__init__(tx_hex, txid, tx_size, offer_p2sh, claimed_p2sh, fee_sats, output_photons)
Parameters:
  • tx_hex (str)

  • txid (str)

  • tx_size (int)

  • offer_p2sh (str)

  • claimed_p2sh (str)

  • fee_sats (int)

  • output_photons (int)

Return type:

None

tx_hex: str
txid: str
tx_size: int
offer_p2sh: str
claimed_p2sh: str
fee_sats: int
output_photons: int
class pyrxd.gravity.ConfirmationStatus[source]

Bases: object

Status returned by GravityTrade.wait_confirmations().

__init__(txid, confirmations, confirmed, block_height)
Parameters:
  • txid (str)

  • confirmations (int)

  • confirmed (bool)

  • block_height (int | None)

Return type:

None

txid: str
confirmations: int
confirmed: bool
block_height: int | None
class pyrxd.gravity.CovenantArtifact[source]

Bases: object

A loaded, pre-compiled covenant artifact with parameter substitution.

__init__(contract, hex_template, abi)
Parameters:
Return type:

None

constructor_params()[source]

Return the constructor ABI params in declaration order.

Return type:

list[dict]

classmethod from_json(json_text, *, allow_legacy=False)[source]

Load from raw artifact JSON (e.g. from a custom compiled artifact).

Parameters:
  • json_text (str)

  • allow_legacy (bool)

Return type:

CovenantArtifact

classmethod load(name, *, allow_legacy=False)[source]

Load a bundled artifact by stem name (without .artifact.json).

Available artifacts: - maker_offer - maker_covenant_6x12_p2wpkh - maker_covenant_flat_6x12_p2wpkh - maker_covenant_trade

Parameters:
Return type:

CovenantArtifact

substitute(params)[source]

Substitute constructor params into the hex template.

Returns the full redeem script bytes. Raises ValidationError if any required param is missing, any placeholder remains unfilled, or any fixed-width typed param (Ripemd160 / Sha256 / PubKey) has the wrong byte length — these would silently encode as the wrong push and produce an on-chain-rejected covenant.

Values: - int params: pass Python int - bytes/Ripemd160/Sha256/PubKey params: pass hex string

Parameters:

params (dict[str, Any])

Return type:

bytes

contract: str
hex_template: str
abi: list[dict]
class pyrxd.gravity.FinalizeResult[source]

Bases: object

FinalizeResult(tx_hex: ‘str’, txid: ‘str’, tx_size: ‘int’, fee_sats: ‘int’, output_photons: ‘int’)

__init__(tx_hex, txid, tx_size, fee_sats, output_photons)
Parameters:
  • tx_hex (str)

  • txid (str)

  • tx_size (int)

  • fee_sats (int)

  • output_photons (int)

Return type:

None

tx_hex: str
txid: str
tx_size: int
fee_sats: int
output_photons: int
class pyrxd.gravity.ForfeitResult[source]

Bases: object

ForfeitResult(tx_hex: ‘str’, txid: ‘str’, tx_size: ‘int’, fee_sats: ‘int’, output_photons: ‘int’)

__init__(tx_hex, txid, tx_size, fee_sats, output_photons)
Parameters:
  • tx_hex (str)

  • txid (str)

  • tx_size (int)

  • fee_sats (int)

  • output_photons (int)

Return type:

None

tx_hex: str
txid: str
tx_size: int
fee_sats: int
output_photons: int
class pyrxd.gravity.GravityMakerSession[source]

Bases: object

Manage the full lifecycle of a Gravity BTC↔RXD atomic swap offer.

This class handles the Maker’s side of the swap:

  1. Build and broadcast the MakerOffer tx (create_offer).

  2. Poll for the Taker’s claim (wait_for_claim).

  3. Broadcast a cancel tx if the Taker never claims (cancel_offer).

  4. Query current state (check_status).

Parameters:
  • rxd_client – Connected ElectrumXClient for Radiant chain operations (broadcast, query UTXOs).

  • btc_source – A BtcDataSource — used only by subclasses / extensions that need BTC confirmation data. May be None for pure Radiant operations.

  • maker_priv – Maker’s secp256k1 private key wrapped in PrivateKeyMaterial.

  • poll_interval_seconds – Seconds between UTXO polls in wait_for_claim. Default 30.

Examples

Typical Maker flow:

async with ElectrumXClient(["wss://electrumx.example.com"]) as rxd:
    session = GravityMakerSession(rxd_client=rxd, maker_priv=priv)
    params = GravityOfferParams(
        offer=offer,
        funding_txid="...",
        funding_vout=0,
        funding_photons=5_100_000,
        fee_sats=100_000,
    )
    active = await session.create_offer(params)
    claim_txid = await session.wait_for_claim(active, timeout_seconds=3600)
    if claim_txid is None:
        cancel_txid = await session.cancel_offer(active)
__init__(rxd_client, maker_priv, btc_source=None, poll_interval_seconds=30)[source]
Parameters:
Return type:

None

async cancel_offer(offer, fee_sats=1000, maker_address='')[source]

Broadcast the cancel (MakerOffer.cancel()) transaction.

Reclaims the MakerOffer UTXO before the claim deadline using build_cancel_tx. This is only valid if the Taker has NOT yet claimed the UTXO.

Parameters:
  • offer (ActiveOffer) – The ActiveOffer to cancel.

  • fee_sats (int) – Miner fee in photons for the cancel tx. Default 1000.

  • maker_address (str) – Maker’s Radiant P2PKH address to receive the reclaimed photons. Required — must be a valid Radiant address.

Returns:

The cancel tx’s txid.

Return type:

str

Raises:
async check_status(offer)[source]

Return the current status of the offer UTXO.

Queries the Radiant ElectrumX server for the MakerOffer P2SH UTXO.

Returns one of:

  • "open" — UTXO is still unspent (offer not yet claimed).

  • "claimed" — UTXO no longer in unspent set (Taker has claimed).

  • "expired" — claim_deadline has passed and UTXO is unspent

    (Maker can now forfeit).

  • "unknown" — UTXO not found and not yet past deadline

    (may be unconfirmed or already finalized/cancelled).

Parameters:

offer (ActiveOffer) – The ActiveOffer to check.

Returns:

One of "open", "claimed", "expired", "unknown".

Return type:

str

Raises:

NetworkError – On ElectrumX query failure.

async create_offer(offer_params)[source]

Build and broadcast the MakerOffer funding tx.

The offer UTXO is a P2SH output locked to offer_params.offer’s MakerOffer covenant. Once broadcast, the Taker can claim it by spending it with build_claim_tx.

Parameters:

offer_params (GravityOfferParams) – Funding-UTXO details and the GravityOffer covenant.

Returns:

Populated with the resulting txid and UTXO details.

Return type:

ActiveOffer

Raises:
async wait_for_claim(offer, timeout_seconds=3600)[source]

Poll for the Taker’s claim transaction.

Polls get_utxos() on the MakerOffer P2SH script hash. When the UTXO disappears from the unspent set the Taker has claimed it.

This method cannot directly return the claim txid — ElectrumX’s listunspent API only reports which UTXOs are currently unspent. Once the offer UTXO is spent (claimed), we return the offer’s txid as a sentinel so the caller knows which offer was claimed. Callers that need the actual claim txid should fetch the spending tx separately (e.g. via get_transaction on the address history).

Parameters:
  • offer (ActiveOffer) – The ActiveOffer returned by create_offer.

  • timeout_seconds (int) – Maximum seconds to wait. Returns None on timeout.

Returns:

The offer txid (as a claimed-sentinel) on success, or None on timeout.

Return type:

str or None

class pyrxd.gravity.GravityOffer[source]

Bases: object

All parameters a Maker commits into a MakerOffer covenant.

Mirrors CovenantParams in pyrxd.spv.proof but adds Radiant-side fields and the two precomputed redeem scripts.

__init__(btc_receive_hash, btc_receive_type, btc_satoshis, chain_anchor, anchor_height, merkle_depth, taker_radiant_pkh, claim_deadline, photons_offered, offer_redeem_hex, claimed_redeem_hex, expected_code_hash_hex)
Parameters:
  • btc_receive_hash (bytes)

  • btc_receive_type (str)

  • btc_satoshis (int)

  • chain_anchor (bytes)

  • anchor_height (int)

  • merkle_depth (int)

  • taker_radiant_pkh (bytes)

  • claim_deadline (int)

  • photons_offered (int)

  • offer_redeem_hex (str)

  • claimed_redeem_hex (str)

  • expected_code_hash_hex (str)

Return type:

None

validate_deadline_from_now(accept_short_deadline=False)[source]

Check that claim_deadline is at least MIN_DEADLINE_FROM_NOW_HOURS from now.

Raises ValidationError unless accept_short_deadline is True (audit 04-S1 guard: Taker needs time to confirm BTC + build SPV proof + finalize on Radiant).

Parameters:

accept_short_deadline (bool)

Return type:

None

btc_receive_hash: bytes
btc_receive_type: str
btc_satoshis: int
chain_anchor: bytes
anchor_height: int
merkle_depth: int
taker_radiant_pkh: bytes
claim_deadline: int
photons_offered: int
offer_redeem_hex: str
claimed_redeem_hex: str
expected_code_hash_hex: str
class pyrxd.gravity.GravityOfferParams[source]

Bases: object

Parameters required to create a new Gravity MakerOffer.

These are the funding-UTXO details for the Maker’s side. The GravityOffer itself (covenant bytecode, BTC-side params, etc.) is built externally (e.g. via build_gravity_offer) and passed as offer.

offer

Fully populated GravityOffer with offer_redeem_hex set.

Type:

pyrxd.gravity.types.GravityOffer

funding_txid

Hex txid of the Maker’s P2PKH UTXO being spent to fund the offer.

Type:

str

funding_vout

Output index of the Maker’s funding UTXO.

Type:

int

funding_photons

Value of the Maker’s funding UTXO in photons.

Type:

int

fee_sats

Miner fee in photons for the MakerOffer funding tx.

Type:

int

change_address

Optional Radiant P2PKH address for change output. See build_maker_offer_tx for semantics.

Type:

str | None

__init__(offer, funding_txid, funding_vout, funding_photons, fee_sats, change_address=None)
Parameters:
Return type:

None

change_address: str | None = None
offer: GravityOffer
funding_txid: str
funding_vout: int
funding_photons: int
fee_sats: int
class pyrxd.gravity.GravityTrade[source]

Bases: object

Orchestrate a complete Gravity BTC↔RXD atomic swap.

Parameters:
  • radiant_network – Connected ElectrumXClient for Radiant chain operations (broadcast, fetch tx/block).

  • bitcoin_source – A BtcDataSource for Bitcoin chain data (tx fetch, Merkle proof, block headers).

  • config – Optional TradeConfig. Uses defaults if not provided.

Examples

Typical Taker flow:

async with ElectrumXClient(["wss://electrumx.example.com"]) as rxd:
    trade = GravityTrade(radiant_network=rxd, bitcoin_source=btc_src)
    claim = await trade.claim(
        offer=offer,
        offer_txid="...",
        offer_vout=0,
        offer_photons=10_000_000,
        fee_sats=1000,
        taker_privkey=privkey,
    )
    btc_txid = "..."  # broadcast BTC payment externally
    status = await trade.wait_confirmations(btc_txid)
    result = await trade.finalize(
        btc_txid=btc_txid,
        offer=offer,
        claimed_txid=claim.txid,
        claimed_vout=0,
        claimed_photons=claim.output_photons,
        taker_address="...",
        fee_sats=1000,
    )
__init__(*, radiant_network, bitcoin_source, config=None)[source]
Parameters:
Return type:

None

async claim(offer, offer_txid, offer_vout, offer_photons, fee_sats, taker_privkey)[source]

Spend the MakerOffer UTXO, creating a MakerClaimed UTXO.

Broadcasts the claim transaction to the Radiant network and returns a ClaimResult.

The claim transaction requires Taker’s signature (audit 04-S3). build_claim_tx independently verifies the code hash before signing (audit 05-F-13).

Parameters:
  • offer (GravityOffer) – The GravityOffer posted by the Maker.

  • offer_txid (str) – Radiant txid of the MakerOffer funding output.

  • offer_vout (int) – Output index of the MakerOffer UTXO.

  • offer_photons (int) – Value of the MakerOffer UTXO in photons.

  • fee_sats (int) – Radiant miner fee in photons.

  • taker_privkey (PrivateKeyMaterial) – Taker’s secp256k1 private key.

Return type:

ClaimResult

async finalize(btc_txid, offer, claimed_txid, claimed_vout, claimed_photons, taker_address, fee_sats, btc_tx_height=None)[source]

Fetch the BTC SPV proof, verify it, and broadcast the finalize tx.

This method always runs the full SpvProofBuilder verifier chain — there is no way to bypass verification at this level.

Parameters:
  • btc_txid (str) – Bitcoin transaction ID of the Taker’s BTC payment.

  • offer (GravityOffer) – The GravityOffer originally posted by the Maker. Used to construct CovenantParams for SPV proof verification.

  • claimed_txid (str) – Radiant txid of the MakerClaimed UTXO (output of claim()).

  • claimed_vout (int) – Output index of the MakerClaimed UTXO.

  • claimed_photons (int) – Value of the MakerClaimed UTXO in photons.

  • taker_address (str) – Taker’s Radiant P2PKH address to receive the photons.

  • fee_sats (int) – Radiant miner fee in photons.

  • btc_tx_height (int | None) – Optional: Bitcoin block height where btc_txid was confirmed. If not provided, the orchestrator will determine it automatically.

Raises:
Return type:

FinalizeResult

async wait_confirmations(btc_txid, min_confirmations=None)[source]

Poll Bitcoin until btc_txid reaches the required confirmations.

Parameters:
  • btc_txid (str) – Bitcoin transaction ID (64 hex chars, big-endian).

  • min_confirmations (int | None) – Override config.min_btc_confirmations for this call.

Returns:

Always has confirmed=True on return (raises on timeout).

Return type:

ConfirmationStatus

Raises:
class pyrxd.gravity.MakerOfferResult[source]

Bases: object

Output of build_maker_offer_tx() — the MakerOffer funding tx.

__init__(tx_hex, txid, tx_size, offer_p2sh, fee_sats, output_photons)
Parameters:
  • tx_hex (str)

  • txid (str)

  • tx_size (int)

  • offer_p2sh (str)

  • fee_sats (int)

  • output_photons (int)

Return type:

None

tx_hex: str
txid: str
tx_size: int
offer_p2sh: str
fee_sats: int
output_photons: int
class pyrxd.gravity.TradeConfig[source]

Bases: object

Tunable parameters for GravityTrade.

min_btc_confirmations

Minimum on-chain BTC confirmations before finalizing. Default 6.

Type:

int

poll_interval_seconds

Seconds between confirmation polls. Default 60.

Type:

float

max_poll_attempts

Maximum number of polls before wait_confirmations raises NetworkError. Default 120 (= 2 hours at 60s).

Type:

int

accept_short_deadline

If True, suppress the 24h deadline guard (audit 04-S1). Only for testing — do NOT set in production.

Type:

bool

deadline_warning_seconds

Emit a WARNING log in finalize() when the Maker’s claim deadline is less than this many seconds away. Default 7200 (2 hours). Set to 0 to disable. Takers should finalize immediately if this fires (audit 04-S1 forfeit race).

Type:

int

__init__(min_btc_confirmations=6, poll_interval_seconds=60, max_poll_attempts=120, accept_short_deadline=False, deadline_warning_seconds=7200)
Parameters:
  • min_btc_confirmations (int)

  • poll_interval_seconds (float)

  • max_poll_attempts (int)

  • accept_short_deadline (bool)

  • deadline_warning_seconds (int)

Return type:

None

accept_short_deadline: bool = False
deadline_warning_seconds: int = 7200
max_poll_attempts: int = 120
min_btc_confirmations: int = 6
poll_interval_seconds: float = 60
pyrxd.gravity.build_claim_tx(offer, funding_txid, funding_vout, funding_photons, fee_sats, taker_privkey, accept_short_deadline=False)[source]

Build the Radiant claim() spending tx: MakerOffer → MakerClaimed.

Requires Taker’s private key to produce a Radiant signature satisfying MakerOffer.claim(takerSig) — prevents third-party state-advance grief (audit 04-S3).

Audit 05-F-13: verifies claimedRedeemHex matches expectedClaimedCodeHash before building, so the tx won’t be rejected on-chain.

scriptSig layout:

<takerSig+hashtype> OP_1 <offer redeem script>
Parameters:
  • offer (GravityOffer) – Fully populated GravityOffer (validated in __post_init__).

  • funding_txid (str) – Hex txid of the MakerOffer UTXO being spent.

  • funding_vout (int) – Output index of the MakerOffer UTXO.

  • funding_photons (int) – Value of the MakerOffer UTXO in photons.

  • fee_sats (int) – Miner fee in photons (== satoshis on Radiant).

  • taker_privkey (PrivateKeyMaterial) – Taker’s secp256k1 private key (wrapped in PrivateKeyMaterial).

  • accept_short_deadline (bool) – If True, suppress the 24-hour deadline guard (audit 04-S1).

Return type:

ClaimResult

pyrxd.gravity.build_finalize_tx(spv_proof, claimed_redeem_hex, funding_txid, funding_vout, funding_photons, to_address, fee_sats, minimum_output_photons=0, header_slots=None, branch_slots=None)[source]

Build the Radiant finalize() tx: MakerClaimed → Taker’s address.

The spv_proof must be a fully-verified SpvProof produced by SpvProofBuilder.build() — this is the only way to construct one.

No Radiant signature is required — the covenant accepts the scriptSig based on the SPV proof data alone. Output routing is enforced by the covenant’s committed takerRadiantPkh state.

scriptSig layout (pushed bottom-to-top; last push is TOP at exec):

<h1> <h2> ... <hN> <branch> <rawTx> OP_0 <claimed redeem script>

OP_0 (empty push = selector 0) selects the finalize() function.

Parameters:
  • spv_proof (SpvProof) – Fully-verified SPV proof (only obtainable from SpvProofBuilder).

  • claimed_redeem_hex (str) – Hex of MakerClaimed locking bytecode.

  • funding_txid (str) – Txid of the MakerClaimed UTXO being spent.

  • funding_vout (int) – Output index of the MakerClaimed UTXO.

  • funding_photons (int) – Value of the MakerClaimed UTXO in photons.

  • to_address (str) – Taker’s Radiant P2PKH address.

  • fee_sats (int) – Miner fee in photons.

  • minimum_output_photons (int) – The covenant’s totalPhotonsInOutput floor — baked in at offer creation time. The finalize tx is rejected on-chain if output[0].value < totalPhotonsInOutput, so we validate here before burning relay fees. Pass offer.photons_offered when calling from GravityTrade. Defaults to 0 (no floor check) for callers that have already verified externally.

  • header_slots (int | None)

  • branch_slots (int | None)

Return type:

FinalizeResult

pyrxd.gravity.build_forfeit_tx(offer, funding_txid, funding_vout, funding_photons, maker_address, fee_sats)[source]

Build the Radiant forfeit() tx: Maker reclaims after claimDeadline.

Can only be built once offer.claim_deadline has passed (i.e. the current wall-clock time is >= claim_deadline).

Sets nLockTime = claim_deadline for OP_CHECKLOCKTIMEVERIFY. Sets input sequence to 0xFFFFFFFE (< 0xFFFFFFFF — required for CLTV to be evaluated).

scriptSig layout:

OP_1 <claimed redeem script>

OP_1 (selector 1) selects the forfeit() function.

Parameters:
  • offer (GravityOffer) – GravityOffer whose claim_deadline has already passed.

  • funding_txid (str) – Txid of the MakerClaimed UTXO being forfeited.

  • funding_vout (int) – Output index of the MakerClaimed UTXO.

  • funding_photons (int) – Value of the MakerClaimed UTXO in photons.

  • maker_address (str) – Maker’s Radiant P2PKH address to receive the reclaimed photons.

  • fee_sats (int) – Miner fee in photons.

Return type:

ForfeitResult

pyrxd.gravity.build_gravity_offer(maker_pkh, maker_pk, taker_pk, taker_radiant_pkh, btc_receive_hash, btc_receive_type, btc_satoshis, btc_chain_anchor, expected_nbits, anchor_height, merkle_depth, claim_deadline, photons_offered, expected_nbits_next=None, accept_short_deadline=False, covenant_artifact_name='maker_covenant_flat_12x20_sentinel_all', offer_artifact_name='maker_offer')[source]

Build a GravityOffer with real covenant redeem scripts generated from the bundled artifacts.

This is the top-level entry point for Maker-side offer construction. Internally it:

  1. Validates the claim deadline (S1 guard).

  2. Loads the MakerClaimed covenant artifact and substitutes code-section params.

  3. Computes expectedClaimedCodeHash = hash256(P2SH_scriptPubKey) from the substituted redeem script.

  4. Loads the MakerOffer artifact and substitutes its params (including the code hash from step 3).

  5. Returns a GravityOffer with both redeem scripts populated.

Parameters:
  • accept_short_deadline (bool) – Override the 24h deadline guard. Set True only for test harnesses you control — never because a counterparty asks.

  • covenant_artifact_name (str) – Override the MakerClaimed artifact stem.

  • offer_artifact_name (str) – Override the MakerOffer artifact stem.

  • maker_pkh (bytes)

  • maker_pk (bytes)

  • taker_pk (bytes)

  • taker_radiant_pkh (bytes)

  • btc_receive_hash (bytes)

  • btc_receive_type (str)

  • btc_satoshis (int)

  • btc_chain_anchor (bytes)

  • expected_nbits (bytes)

  • anchor_height (int)

  • merkle_depth (int)

  • claim_deadline (int)

  • photons_offered (int)

  • expected_nbits_next (bytes | None)

Return type:

Any

pyrxd.gravity.build_maker_offer_tx(offer, funding_txid, funding_vout, funding_photons, fee_sats, maker_privkey, change_address=None)[source]

Build the Radiant funding tx that deploys a MakerOffer P2SH UTXO.

Spends a plain P2PKH UTXO owned by the Maker and creates a P2SH output locked to the MakerOffer redeem script. Once confirmed, the Taker can spend it with build_claim_tx().

The P2SH scriptPubKey is:

OP_HASH160 <hash160(offer_redeem)> OP_EQUAL

Signing uses standard BIP143 P2PKH sighash (the input is a plain P2PKH UTXO, not a covenant) with Radiant’s hashOutputHashes extension. The scriptCode for signing is the P2PKH scriptPubKey of the funding input, derived from the Maker’s compressed public key.

Parameters:
  • offer (GravityOffer) – Fully populated GravityOffer with offer_redeem_hex set.

  • funding_txid (str) – Hex txid of the Maker’s P2PKH UTXO being spent.

  • funding_vout (int) – Output index of the Maker’s P2PKH UTXO.

  • funding_photons (int) – Value of the Maker’s P2PKH UTXO in photons.

  • fee_sats (int) – Miner fee in photons. The offer output receives funding_photons - fee_sats photons.

  • maker_privkey (PrivateKeyMaterial) – Maker’s secp256k1 private key (PrivateKeyMaterial). Used to sign the P2PKH input and derive the P2PKH scriptCode for hashing.

  • change_address (str | None) – Default None (single-output): the full funding_photons - fee_sats is locked in the P2SH, so surplus above offer.photons_offered stays with the covenant to fund the later claim/finalize tx fees. When set (two-output): the P2SH receives exactly offer.photons_offered and the remainder goes to a P2PKH output at change_address. Use the two-output form only when offer.photons_offered already includes a buffer for downstream claim/finalize fees — otherwise the covenant will reject those txs.

Return type:

MakerOfferResult

pyrxd.gravity.compute_p2sh_code_hash(redeem_script)[source]

Compute expectedClaimedCodeHash: hash256 of the P2SH scriptPubKey.

This is what MakerOffer checks on-chain:

hash256(tx.outputs[0].codeScript) == expectedClaimedCodeHash

For P2SH outputs the codeScript is the 23-byte OP_HASH160 <hash> OP_EQUAL scriptPubKey.

Audit 05-F-13 fix: caller passes the claimed redeem script; we derive the hash independently rather than trusting a caller-supplied value.

Parameters:

redeem_script (bytes)

Return type:

bytes

pyrxd.gravity.validate_claim_deadline(claim_deadline, *, min_future_seconds=86400, bypass=False)[source]

Raise ValidationError if claim_deadline is not at least min_future_seconds from now (default: 24h).

This is the Python port of the S1 check in extract_p2sh_code_hash.js (audit 04 finding S1: a short deadline lets Maker race-snipe Taker’s claim).

Parameters:
  • bypass (bool) – Only set True for test harnesses you control. Never set because a counterparty asked you to.

  • claim_deadline (int)

  • min_future_seconds (int)

Return type:

None