Trade NFTs on the XRP Ledger without trusting the counterparty. Payment locks in escrow when the buyer commits — it releases automatically only once the NFT is confirmed in the buyer's wallet. Neither party can take the money and run.
Standard NFT trades on any chain carry counterparty risk: the buyer pays first and hopes the NFT arrives, or the seller transfers first and hopes payment follows. Off-chain agreements, trusted escrow services, and multi-sig setups all require some form of trust.
XRPL's native escrow operates at the protocol layer — not a smart contract, the base ledger itself. Combined with the AgentTrust referee, you get a two-sided atomic trade:
Buyer funds are held in XRPL crypto-condition escrow at the moment of commitment — not held by any platform, not in a hot wallet, locked in the ledger itself.
The seller submits proof of NFT transfer (the on-chain token ID in the buyer's wallet). The AI referee verifies the chain state and releases payment automatically.
Buyer can't receive the NFT and claim non-delivery. Seller can't receive payment and not transfer. The escrow enforces the deal atomically.
XRPL native escrow costs fractions of a cent — not $5–50 in gas. Protocol fee: 0.1 XRP per escrow (~$0.20). Everything else runs on-ledger at negligible cost.
What the AI referee checks: The task description you write into the escrow becomes the verification spec. Write it as: "NFT [token ID] issued by [issuer address] must be owned by [buyer address] at the time of evaluation." The referee queries the XRPL ledger directly and checks the current owner of that token ID.
Buyer and seller agree on the XRPL NFT token ID (a 64-character hex string, e.g. 000800006B6B…), the price in XRP or RLUSD, and a cancellation window (e.g. 72 hours if the NFT never arrives). The token ID uniquely identifies the specific NFT on the ledger.
The buyer pays the 0.1 XRP protocol fee and calls POST /escrow/generate with the task description encoding the expected NFT ownership. The resulting EscrowCreate transaction is submitted on-chain, locking the funds.
Seller uses XRPL's NFTokenCreateOffer / NFTokenAcceptOffer flow to transfer the NFT to the buyer's wallet, then calls POST /evaluate with the token ID as proof. On PASS, payment releases automatically.
Security: Never put a wallet seed in browser JavaScript. Run this server-side or in your agent's trusted environment.
# pip install xrpl-py httpx
import httpx, secrets
from xrpl.clients import JsonRpcClient
from xrpl.models.transactions import Payment, EscrowCreate
from xrpl.wallet import Wallet
from xrpl.utils import xrp_to_drops
from xrpl.transaction import submit_and_wait
REFEREE = "https://xrpl-referee.onrender.com"
PROTOCOL_FEE = "rmcSrkpZ2i2kuvtCPeTVetee9SixP4djR"
XRPL_NODE = "https://s1.ripple.com:51234/"
buyer_wallet = Wallet.from_seed("sBUYER_SEED_HERE")
seller_address = "rSELLER_ADDRESS_HERE"
client = JsonRpcClient(XRPL_NODE)
# Agree these off-chain with the seller:
NFT_TOKEN_ID = "000800006B6B..." # 64-char hex NFT token ID
PRICE_XRP = 50.0
CANCEL_HRS = 72
# Build the task description — this is what the referee will verify
task_desc = (
f"NFT Delivery-vs-Payment escrow.\n"
f"NFT token ID: {NFT_TOKEN_ID}\n"
f"Must be owned by buyer wallet: {buyer_wallet.address}\n"
f"Payment releases when the NFT is confirmed in the buyer's wallet."
)
# Step 1: Pay the 0.1 XRP protocol fee
fee_tx = Payment(
account = buyer_wallet.address,
amount = xrp_to_drops(0.1),
destination = PROTOCOL_FEE,
)
fee_hash = submit_and_wait(fee_tx, client, buyer_wallet).result["hash"]
print(f"Protocol fee tx: {fee_hash}")
# Step 2: Generate escrow parameters from AgentTrust
escrow_id = f"NFT-{secrets.token_hex(4).upper()}"
params = httpx.post(f"{REFEREE}/escrow/generate", json={
"escrow_id": escrow_id,
"fee_hash": fee_hash,
"buyer_name": "Alice",
"buyer_address": buyer_wallet.address,
"worker_address": seller_address,
"task_description": task_desc,
"amount_xrp": PRICE_XRP,
"cancel_after_hrs": CANCEL_HRS,
}, timeout=30).json()
# Step 3: Submit the EscrowCreate on-chain
escrow_tx = EscrowCreate(
account = buyer_wallet.address,
destination = seller_address,
amount = xrp_to_drops(PRICE_XRP),
condition = params["condition"],
finish_after = params["finish_after_ripple"],
cancel_after = params["cancel_after_ripple"],
)
tx_hash = submit_and_wait(escrow_tx, client, buyer_wallet).result["hash"]
print(f"Escrow created: {tx_hash}")
# Step 4: Confirm the escrow with AgentTrust (starts the clock)
httpx.post(f"{REFEREE}/escrow/{escrow_id}/confirm",
json={"tx_hash": tx_hash}).raise_for_status()
print(f"Escrow ID: {escrow_id} — share this with the seller")
Flow: Transfer the NFT to the buyer's XRPL wallet first, then call /evaluate with the token ID as your proof. The referee checks on-chain that the NFT is now owned by the buyer before releasing payment.
# pip install xrpl-py httpx
import httpx
from xrpl.clients import JsonRpcClient
from xrpl.models.transactions import NFTokenCreateOffer, NFTokenAcceptOffer
from xrpl.models.requests import AccountNFTs
from xrpl.wallet import Wallet
from xrpl.transaction import submit_and_wait
from xrpl.utils import xrp_to_drops
REFEREE = "https://xrpl-referee.onrender.com"
XRPL_NODE = "https://s1.ripple.com:51234/"
seller_wallet = Wallet.from_seed("sSELLER_SEED_HERE")
buyer_address = "rBUYER_ADDRESS_HERE"
client = JsonRpcClient(XRPL_NODE)
ESCROW_ID = "NFT-XXXX" # escrow_id shared by the buyer
NFT_TOKEN_ID = "000800006B6B..."
# Step 1: Create a sell offer for 0 XRP (free transfer to specific buyer)
offer_tx = NFTokenCreateOffer(
account = seller_wallet.address,
nftoken_id = NFT_TOKEN_ID,
amount = "0", # buyer already paid via escrow
destination = buyer_address, # restricted to the buyer only
flags = 1, # tfSellNFToken
)
offer_result = submit_and_wait(offer_tx, client, seller_wallet).result
offer_id = offer_result["meta"]["offer_id"]
print(f"Sell offer created: {offer_id}")
# Step 2: Buyer accepts the offer (can be a separate script run by buyer)
# buyer_wallet = Wallet.from_seed("sBUYER_SEED")
# accept_tx = NFTokenAcceptOffer(
# account=buyer_wallet.address, nftoken_sell_offer=offer_id)
# submit_and_wait(accept_tx, client, buyer_wallet)
# -- (once buyer has accepted and NFT is in their wallet) --
# Step 3: Verify the NFT is now in the buyer's wallet
nfts = client.request(AccountNFTs(account=buyer_address)).result["account_nfts"]
owned = any(n["NFTokenID"] == NFT_TOKEN_ID for n in nfts)
assert owned, "NFT not yet in buyer wallet — wait for acceptance"
print("NFT confirmed in buyer wallet. Submitting proof...")
# Step 4: Submit proof to AgentTrust — payment releases automatically on PASS
result = httpx.post(f"{REFEREE}/evaluate", json={
"escrow_id": ESCROW_ID,
"work": (
f"NFT transfer complete.\n"
f"Token ID: {NFT_TOKEN_ID}\n"
f"Current owner (buyer): {buyer_address}\n"
f"Sell offer accepted: {offer_id}\n"
f"NFT is confirmed in the buyer's wallet on the XRP Ledger."
),
}, timeout=120).json()
print(f"Verdict: {result['verdict']} | Score: {result['score']}/100")
# PASS → EscrowFinish submitted on-chain automatically → seller receives XRP
Two-sided acceptance: On XRPL, both parties need to act: the seller creates a sell offer, the buyer accepts it. In a fully automated flow, both sides run their respective code. In a manual flow, share the offer_id with the buyer so they can accept it with Xaman or any XRPL wallet.
The task description is the verification spec the AI referee scores against. For NFT DvP trades, be explicit about exactly what constitutes proof of delivery:
NFT Delivery-vs-Payment escrow.
NFT token ID: 000800006B6B4C70EDB8EBC90F2A5EDB45FDBA
Issuer address: rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh
Buyer wallet: rBuyer123...
Seller wallet: rSeller456...
Agreed price: 50 XRP (held in escrow, released on verification)
PAYMENT RELEASES when the submission confirms that:
- The NFT with the above token ID is owned by the buyer wallet
- The transfer has been accepted and finalized on the XRP Ledger mainnet
Seller should include: the sell offer ID, confirmation that the
buyer accepted it, and a statement that the NFT is in the buyer's wallet.
Cancel window matters: Set cancel_after_hrs to give the seller enough time to list, transfer, and have the buyer accept the NFT. 72 hours is reasonable for a manually coordinated trade. If the deadline passes without a PASS verdict, the buyer can reclaim the escrowed funds. The NFT offer will remain open unless explicitly cancelled.
AgentTrust lets you add a trust condition requiring the seller wallet to hold an NFT from a specific issuer before the escrow can be created. This is useful for gating trades to verified platforms, credentialed creators, or whitelisted collections.
{
"escrow_id": "NFT-TRADE-001",
"fee_hash": "...",
"buyer_address": "rBuyer...",
"worker_address": "rSeller...",
"task_description": "...",
"amount_xrp": 50,
"cancel_after_hrs": 72,
// Require seller to hold a verified-creator NFT from this issuer
"required_nft_issuer": "rISSUER_ADDRESS",
"required_nft_taxon": 12 // optional: restrict to a specific collection
}
If the seller's wallet doesn't hold a matching NFT, escrow creation is blocked and a clear error is returned. Combine this with domain verification and OFAC screening for a fully trust-gated trade.
If you want to denominate the trade in USD rather than XRP, use the currency: "RLUSD" field. RLUSD is Ripple's regulated stablecoin on the XRP Ledger, pegged 1:1 to the US dollar.
{
"escrow_id": "NFT-USD-001",
"fee_hash": "...",
"buyer_address": "rBuyer...",
"worker_address": "rSeller...",
"task_description": "...",
"currency": "RLUSD",
"amount_rlusd": 500.00, // $500 USD
"cancel_after_hrs": 72
}
Creator sells a 1-of-1 or edition NFT. Buyer's payment locks before the transfer — neither side can ghost after committing.
Event tickets issued as XRPL NFTs. Resale market with atomic settlement — buyer gets the ticket, seller gets paid, simultaneously.
Tokenised real-world assets (property fractions, carbon credits, trade finance instruments) settling with cryptographic finality.
AI agents trading NFT-represented licences, data access tokens, or compute credits autonomously — payment and delivery without human approval.