# XRPL JavaScript 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>.js` (e.g., `createLoanBroker.js`) | A dev reading & learning the protocol | Clarity over speed |
| **Setup** | `<topic>Setup.js` (e.g., `lendingSetup.js`) | 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

- 2-space indent
- Single quotes
- No semicolons


### Naming

- File names: `camelCase` (e.g., `createLoan.js`)
- Variables: `camelCase` with acronyms uppercased — `loanBroker`, `mptID`, `vaultID`, `loanBrokerID`, `credentialIssuer`
- Transaction object keys: XRPL native PascalCase (`TransactionType`, `Account`, `Amount`) — never transform them
- Setup JSON keys: `camelCase` (`loanBroker`, `credentialIssuer`, `mptID`, `vaultID`, `loanBrokerID`)


## Structure

### Folder layout

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


```
_code-samples/<topic>/js/
├── README.md
├── package.json
├── package-lock.json
├── <topic>Setup.js          # Optional — runs once to prep network state
├── <topic>Setup.json        # Auto-generated by the setup script and is gitignored
└── <verb><Thing>.js         # Tutorial scripts (one per user action)
```

### README

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

1. Title: `# <Topic> Examples (JavaScript)`
2. One-sentence description listing what the directory demonstrates
3. `## Setup` section with a single `npm i` fenced block
4. 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 `node <file>.js`
- 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.

### package.json

Minimal — no `scripts`, no `devDependencies`, no `version` unless an external dep requires one:


```json
{
  "name": "<topic>-examples",
  "description": "Example code for <one-line summary>.",
  "dependencies": {
    "xrpl": "^<latest-stable>"
  },
  "type": "module"
}
```

## Tutorial files

### 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
3. Connect to the network:



```js
// Connect to the network ----------------------
const client = new xrpl.Client('wss://s.devnet.rippletest.net:51233')
await client.connect()
```

1. (Optional) If the tutorial is using setup script data:



```js
// This step checks for the necessary setup data to run the lending tutorials.
// If missing, lendingSetup.js will generate the data.
if (!fs.existsSync('lendingSetup.json')) {
  console.log(`\n=== Lending tutorial data doesn't exist. Running setup script... ===\n`)
  execSync('node lendingSetup.js', { stdio: 'inherit' })
}

// Load preconfigured accounts and VaultID.
const setupData = JSON.parse(fs.readFileSync('lendingSetup.json', 'utf8'))

// You can replace these values with your own
const loanBroker = xrpl.Wallet.fromSeed(setupData.loanBroker.seed)
const vaultID = setupData.vaultID

console.log(`\nLoan broker/vault owner address: ${loanBroker.address}`)
console.log(`Vault ID: ${vaultID}`)
```

1. (Optional) If no setup data is used, fund new wallets for the tutorial.



```js
const { wallet } = await client.fundWallet()
```

If creating multiple wallets, parallelize the process:


```js
const [
  { wallet: loanBroker },
  { wallet: borrower },
  { wallet: depositor },
  { wallet: credentialIssuer }
] = await Promise.all([
  client.fundWallet(),
  client.fundWallet(),
  client.fundWallet(),
  client.fundWallet()
])
```

1. Tutorial code steps.
2. Disconnect at the end of the code sample.



```js
await client.disconnect()
```

### Tutorial code step guide

- Before each major step, add a comment and print a section banner.
- Build transactions as plain object literals and validate before submitting:

```js
// Prepare LoanBrokerSet transaction ----------------------
console.log(`\n=== Preparing LoanBrokerSet transaction ===\n`)
const loanBrokerSetTx = {
  TransactionType: 'LoanBrokerSet',
  Account: loanBroker.address,
  VaultID: vaultID,
  ManagementFeeRate: 1000
}

// Validate the transaction structure before submitting
xrpl.validate(loanBrokerSetTx)
console.log(JSON.stringify(loanBrokerSetTx, null, 2))
```
- Autofill transactions and handle results by checking for `tesSUCCESS` and exiting on failure:

```js
// Submit, sign, and wait for validation ----------------------
console.log(`\n=== Submitting LoanBrokerSet transaction ===\n`)
const submitResponse = await client.submitAndWait(loanBrokerSetTx, {
  wallet: loanBroker,
  autofill: true
})
if (submitResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
  const resultCode = submitResponse.result.meta.TransactionResult
  console.error('Error: Unable to create loan broker:', resultCode)
  await client.disconnect()
  process.exit(1)
}
console.log('Loan broker created successfully!')
```
- Extract metadata relevant to the tutorial:

```js
// Extract loan broker information from the transaction result
console.log(`\n=== Loan Broker Information ===\n`)
const loanBrokerNode = submitResponse.result.meta.AffectedNodes.find(node =>
  node.CreatedNode?.LedgerEntryType === 'LoanBroker'
)
console.log(`LoanBroker ID: ${loanBrokerNode.CreatedNode.LedgerIndex}`)
console.log(`LoanBroker Pseudo-Account Address: ${loanBrokerNode.CreatedNode.NewFields.Account}`)
```


## Setup files

### Speed-first patterns when possible

- Run independent transactions concurrently with `await Promise.all([...])`
- When fanning out parallel transactions from the same account, batch them first via `TicketCreate` with `TicketCount: N`, then pass `Sequence: 0` and `TicketSequence: ticketArr[i]` on each parallel tx
- Destructure response arrays: `const [{ wallet: loanBroker }, { wallet: borrower }] = await Promise.all([client.fundWallet(), client.fundWallet()])`


### Setup code guide

- Top comment: single line, `// Setup script for <topic> tutorials`
- Only output is a carriage-return progress indicator: `process.stdout.write('Setting up tutorial: N/D\r')` between phases, where N is the step number and D is the total steps
- No `=== Section ===` banners, no `xrpl.validate(tx)`, no transaction dumps — the user never sees this file's output beyond the progress counter
- Section comments in code are short: `// Section description` (no dash visual)
- If a library call emits a warning the reader doesn't need (e.g., `LoanSet` autofill warning), silence it locally with a one-line comment explaining why: `console.warn = () => {}`


### Output file

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


```js
const setupData = {
  description: 'This file is auto-generated by lendingSetup.js. It stores XRPL account info for use in lending protocol tutorials.',
  loanBroker: {
    address: loanBroker.address,
    seed: loanBroker.seed
  },
  domainID,
  mptID
}

fs.writeFileSync('lendingSetup.json', JSON.stringify(setupData, null, 2))
```