Payment predicate
The guest verifies one recipient output and derives its transaction ID and output key.
Supported encoding
Section titled “Supported encoding”Recipients are 95 ASCII bytes for a Monero mainnet standard address or subaddress, with valid Base58 encoding, checksum, prefix, and canonical nonidentity prime-order spend/view keys.
Payments require transaction version 2, unlock_time = 0, RingCT type 6 (BulletproofPlus), tagged-key outputs, and 16 ring members per input. Parsing consumes each component exactly and rejects ambiguous/duplicate extra-key encodings and invalid transaction public keys.
Transaction ID and key selection
Section titled “Transaction ID and key selection”txid = Keccak256( Keccak256(payment_prefix) || Keccak256(rct_base) || prunable_hash)Hash raw 32-byte values, without byte reversal; Keccak-256 is not SHA3-256. The output index must exist. key_selector = 0 selects the primary key; 1 selects that output’s additional key. The selected key and shared point must be nonidentity prime-order points.
Swap-bound OutProofV2
Section titled “Swap-bound OutProofV2”Let R be the selected transaction key, A the recipient view key, B its spend key, D the shared point, and G the Edwards base point. Scalars c and r must be canonical.
message = ASCII("ETH-XMR-SWAP/1:") || lowercase_hex(swap_id)m = Keccak256(raw_txid || message)proof_base = G for standard addresses; B for subaddressesX = cR + r·proof_baseY = cD + rAThe swap hex is 64 lowercase digits without 0x. The challenge equals hash_to_scalar of the ordered concatenation:
m || compress(D) || compress(X) || compress(Y)|| Keccak256("TXPROOF_V2") || compress(R) || compress(A) || spend_fieldspend_field is 32 zero bytes for standard addresses or compress(B) for subaddresses. The witness carries the decoded binary tuple, not the Base58 envelope.
Selected output and amount
Section titled “Selected output and amount”Compute derivation = compress(8D) and append the canonical varint output index; hashing to a scalar gives s. Require:
- Output key
B + sG, transparent amount zero, and the correct first-byteview_tagdomain hash. - Eight amount bytes unmasked with the
amountdomain hash ofs, decoded as little-endianuint64piconero, at least the winning quote. - A matching RingCT commitment using the
commitment_maskdomain hash and commitment baseH.
One output must cover the entire quote; smaller outputs are not summed. Change outputs are permitted. See security for the work-based model and checks outside this predicate.