Files
xrpl-dev-portal/.claude/rules/python-code-samples.md
2026-06-05 12:13:01 -07:00

7.8 KiB

paths
paths
_code-samples/**/*.py

XRPL Python Code Sample Conventions

Code samples come in two flavors with very different conventions. Identify which you're writing first.

Flavor Filename pattern Audience Priority
Tutorial <verb>_<thing>.py (e.g., create_loan_broker.py) A dev reading & learning the protocol Clarity over speed
Setup <topic>_setup.py (e.g., lending_setup.py) A dev who never opens this file — runs to prep network data (accounts, tokens, etc.) for all tutorials in the subject folder Speed over clarity

If a file isn't clearly one or the other, prompt the user for clarity.

Style

Formatting

  • 4-space indent
  • Double quotes
  • Trailing commas on multi-line collections and call args

Naming

  • File names: snake_case (e.g., create_loan.py)
  • Variables: snake_caseloan_broker, mpt_id, vault_id, loan_broker_id, credential_issuer
  • Transaction model fields: snake_case per xrpl-py (e.g., account=, vault_id=, management_fee_rate=) — xrpl-py handles the PascalCase translation on the wire
  • Setup JSON keys: snake_case (loan_broker, credential_issuer, mpt_id, vault_id, loan_broker_id)

Structure

Folder layout

Each code sample lives at _code-samples/<topic>/py/:

_code-samples/<topic>/py/
├── README.md
├── requirements.txt
├── <topic>_setup.py        # Optional — runs once to prep network state
├── <topic>_setup.json      # Auto-generated by the setup script and is gitignored
└── <verb>_<thing>.py       # Tutorial scripts (one per user action)

README

README.md is the entry point for a reader running the samples.

  1. Title: # <Topic> Examples (Python)
  2. One-sentence description listing what the directory demonstrates
  3. ## Setup section with a single fenced block showing venv creation + pip install -r requirements.txt:
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
  1. One ## section per tutorial script, in the order a reader should run them:
  • Heading describes the action (e.g., ## Create a Loan Broker), not the filename
  • Fenced sh block with python3 <file>.py
  • One-sentence summary of what the script will output
  • Fenced sh block showing actual expected console output (real addresses, tx IDs, JSON dumps — captured from a successful sample code run)
  1. --- separator between tutorial sections

The expected-output blocks document the golden path. Update them when a script's output format changes.

requirements.txt

Minimal — pin only what's needed:

xrpl-py>=<latest-stable>

Add other deps only when a sample requires them.

Tutorial files

Sync API onlyxrpl.clients.JsonRpcClient, xrpl.transaction.submit_and_wait, xrpl.wallet.Wallet, xrpl.wallet.generate_faucet_wallet. No asyncio, no main() wrapper, no if __name__ == "__main__": — scripts run top-to-bottom and exit.

Structure

  1. Multi-line # IMPORTANT: header explaining what the script demonstrates and any preconditions (e.g., "uses an existing account that has a PRIVATE vault")
  2. Imports — stdlib first, blank line, then xrpl imports
  3. Set up the client:
# Set up client ----------------------
client = JsonRpcClient("https://s.devnet.rippletest.net:51234")
  1. (Optional) If the tutorial is using setup script data:
# This step checks for the necessary setup data to run the lending tutorials.
# If missing, lending_setup.py will generate the data.
if not os.path.exists("lending_setup.json"):
    print("\n=== Lending tutorial data doesn't exist. Running setup script... ===\n")
    subprocess.run([sys.executable, "lending_setup.py"], check=True)

# Load preconfigured accounts and vault_id.
with open("lending_setup.json") as f:
    setup_data = json.load(f)

# You can replace these values with your own.
loan_broker = Wallet.from_seed(setup_data["loan_broker"]["seed"])
vault_id = setup_data["vault_id"]

print(f"\nLoan broker/vault owner address: {loan_broker.address}")
print(f"Vault ID: {vault_id}")
  1. (Optional) If no setup data is used, fund new wallets for the tutorial.
wallet = generate_faucet_wallet(client)

Funding multiple wallets requires sequential calls. 6. Tutorial code steps.

Tutorial code step guide

  • Before each major step, add a comment and print a section banner.
  • Build transactions as xrpl-py model instances and print the wire form before submitting:
    # Prepare LoanBrokerSet transaction ----------------------
    print("\n=== Preparing LoanBrokerSet transaction ===\n")
    loan_broker_set_tx = LoanBrokerSet(
        account=loan_broker.address,
        vault_id=vault_id,
        management_fee_rate=1000,
    )
    
    print(json.dumps(loan_broker_set_tx.to_xrpl(), indent=2))
    
  • Submit with submit_and_wait and handle results by checking for tesSUCCESS and exiting on failure:
    # Submit, sign, and wait for validation ----------------------
    print("\n=== Submitting LoanBrokerSet transaction ===\n")
    submit_response = submit_and_wait(loan_broker_set_tx, client, loan_broker)
    
    if submit_response.result["meta"]["TransactionResult"] != "tesSUCCESS":
        result_code = submit_response.result["meta"]["TransactionResult"]
        print(f"Error: Unable to create loan broker: {result_code}")
        sys.exit(1)
    
    print("Loan broker created successfully!")
    
  • Extract metadata relevant to the tutorial:
    # Extract loan broker information from the transaction result
    print("\n=== Loan Broker Information ===\n")
    loan_broker_node = next(
        node for node in submit_response.result["meta"]["AffectedNodes"]
        if node.get("CreatedNode", {}).get("LedgerEntryType") == "LoanBroker"
    )
    print(f"LoanBroker ID: {loan_broker_node['CreatedNode']['LedgerIndex']}")
    print(f"LoanBroker Psuedo-Account Address: {loan_broker_node['CreatedNode']['NewFields']['Account']}")
    

Setup files

Async API onlyxrpl.asyncio.clients.AsyncWebsocketClient, xrpl.asyncio.wallet.generate_faucet_wallet, xrpl.asyncio.transaction (submit_and_wait, autofill, sign). Wrap in async def main(): + async with AsyncWebsocketClient(WSS_URL) as client:, and call asyncio.run(main()) at the bottom.

WebSocket endpoints: Devnet (wss://s.devnet.rippletest.net:51233) or Testnet (wss://s.altnet.rippletest.net:51233).

Speed-first patterns when possible

  • Run independent transactions concurrently with await asyncio.gather(...)
  • When fanning out parallel transactions from the same account, batch them first via TicketCreate(ticket_count=N), then pass sequence=0 and ticket_sequence=... on each parallel tx
  • Destructure gather results: loan_broker, borrower = await asyncio.gather(generate_faucet_wallet(client), generate_faucet_wallet(client))
  • Group xrpl.models imports into a single alphabetized parenthesized block

Setup code guide

  • Top comment: single line, # Setup script for <topic> tutorials
  • Only output is a carriage-return progress indicator: print("Setting up tutorial: N/D", end="\r") between phases, where N is the step number and D is the total steps
  • No === Section === banners, no transaction dumps — the reader never sees this file's output beyond the progress counter
  • Section comments in code are short: # Section description (no dash visual)

Output file

At the end, write all data the tutorials will need:

setup_data = {
    "description": "This file is auto-generated by lending_setup.py. It stores XRPL account info for use in lending protocol tutorials.",
    "loan_broker": {
        "address": loan_broker.address,
        "seed": loan_broker.seed,
    },
    "domain_id": domain_id,
    "mpt_id": mpt_id,
}

with open("lending_setup.json", "w") as f:
    json.dump(setup_data, f, indent=2)