This tutorial shows how to look up outstanding escrows in the XRP Ledger by their sender or recipient's address.
By following this tutorial, you should learn how to:
- Read a paginated response from the account_objects method.
- Identify when an escrow is mature or if it has expired.
To complete this tutorial, you should:
- Have a basic understanding of the XRP Ledger.
- Have an XRP Ledger client library, such as xrpl.js, installed.
From the code sample folder, use npm to install dependencies:
npm iTo get started, import the client library and instantiate an API client. For this tutorial, you also need the address of an account that has one or more pending escrows. The sample code uses a Mainnet address that has been set up with at least one incoming and one outgoing escrow that should remain in place until 2035 at least.
import { Client, dropsToXrp, rippleTimeToISOTime } from 'xrpl'
// Set up client and address
const address = 'rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn'
console.log('Connecting to Mainnet...')
const client = new Client('wss://xrplcluster.com/')
await client.connect()To know if escrows have matured or expired, you need to know the official close time of the most recently validated ledger. Use the ledger method to get the latest validated ledger, and save its close_time and ledger_hash values to use when looking up escrows.
// Look up the official close time of the validated ledger ---------------------
const ledger = await client.request({
command: 'ledger',
ledger_index: 'validated'
})
if (ledger.error) {
console.error(`Error looking up validated ledger: ${ledger.error}`)
client.disconnect()
process.exit(1)
}
const close_time = ledger.result.ledger.close_time
const ledger_hash = ledger.result.ledger.ledger_hashTo get a list of all escrows linked to an account, use the account_objects method with the results filtered to the escrow type. The list may be paginated, so you may need to make multiple requests to get them all, depending on how many objects are linked to the account. Due to the way filtering works, some pages may be empty even if there are more escrows on later pages.
// Look up objects filtered to escrows, handling pagination --------------------
let marker
const escrows = []
while (true) {
console.log(`Requesting page of account_objects with marker ${marker}`)
const resp = await client.request({
command: 'account_objects',
account: address,
ledger_hash, // Caution: if you use a shortcut
// such as "validated", the ledger may
// change during iteration, leading to
// inconsistent results.
type: 'escrow',
marker
})
if (resp.error) {
console.error('account_objects failed with error', resp)
client.disconnect()
process.exit(1)
}
// Add new escrows to the full list
for (const escrow of resp.result.account_objects) {
escrows.push(escrow)
}
// If there's a marker, loop and fetch the next page of results
if (resp.result.marker) {
marker = resp.result.marker
} else {
break
}
}With
Requires the TokenEscrow. Loading...
, escrows can send three types of funds: XRP, trust line tokens, or multi-purpose tokens. To help handle all three types of amount object, create a function that takes an unknown amount value from the ledger and returns a string containing a display value that is more human-readable.// Define helper function for displaying amounts -------------------------------
function display_amount (amount) {
if (typeof amount === 'string') {
// amount is drops of XRP.
const decimal_xrp = dropsToXrp(amount)
return `${decimal_xrp} XRP`
} else if (amount.hasOwnProperty('mpt_issuance_id')) {
// amount is an MPT.
// More info may be available, but that would require looking it up.
return `${amount.value} units of MPT ${amount.mpt_issuance_id}`
} else if (amount.hasOwnProperty('issuer')) {
// amount is a trust line token.
// Currency may be 3 chars or hex. For guidelines parsing hex codes,
// see "Normalize Currency Codes" code sample.
return `${amount.value} ${amount.currency} issued by ${amount.issuer}`
}
console.error(`Unexpected type of amount: ${amount}`)
client.disconnect()
process.exit(1)
}Each entry in the list you built earlier should be an [Escrow ledger entry][] sent either to or from the account whose address you specified when looking them up. Use the fields of those objects to display more information about the escrows.
For the FinishAfter (maturity time) and CancelAfter (expiration time) fields, compare them to the official close time of the most recently validated ledger to see if the escrow has matured or expired, respectively. These fields are formatted natively as seconds since the Ripple Epoch, so be sure to convert them to a more human-readable format for display.
// Summarize results -----------------------------------------------------------
console.log(`Found ${escrows.length} escrow(s).`)
for (const escrow of escrows) {
if (escrow.Account === address) {
console.log(`Outgoing escrow to ${escrow.Destination}`)
} else if (escrow.Destination === address) {
console.log(`Incoming escrow from ${escrow.Account}`)
} else {
console.log('Neither incoming nor outgoing? This is unexexpected.')
}
console.log(` Amount: ${display_amount(escrow.Amount)}`)
if (escrow.hasOwnProperty('Condition')) {
console.log(` Condition: ${escrow.Condition}`)
}
if (escrow.FinishAfter) {
const mature_time_display = rippleTimeToISOTime(escrow.FinishAfter)
if (escrow.FinishAfter < close_time) {
console.log(` Matured at ${mature_time_display}`)
} else {
console.log(` Will mature at ${mature_time_display}`)
}
}
if (escrow.hasOwnProperty('CancelAfter')) {
const cancel_time_display = rippleTimeToISOTime(escrow.CancelAfter)
if (escrow.CancelAfter < close_time) {
console.log(` EXPIRED at ${cancel_time_display}`)
} else {
console.log(` Expires at ${cancel_time_display}`)
}
}
}
client.disconnect()When done, disconnect the WebSocket client.