From 242a6cc31b5fd8b937bb094375f7faddd403b5fb Mon Sep 17 00:00:00 2001 From: mDuo13 Date: Fri, 15 Aug 2025 16:30:42 -0700 Subject: [PATCH 01/12] Show two possible formats of Escrow sample code --- _code-samples/escrow/js/create-escrow.js | 69 --------- _code-samples/escrow/js/package.json | 7 +- .../escrow/js/send-timed-escrow-functions.js | 146 ++++++++++++++++++ .../escrow/js/send-timed-escrow-linear.js | 92 +++++++++++ 4 files changed, 242 insertions(+), 72 deletions(-) delete mode 100644 _code-samples/escrow/js/create-escrow.js create mode 100644 _code-samples/escrow/js/send-timed-escrow-functions.js create mode 100644 _code-samples/escrow/js/send-timed-escrow-linear.js diff --git a/_code-samples/escrow/js/create-escrow.js b/_code-samples/escrow/js/create-escrow.js deleted file mode 100644 index 6d557475b69..00000000000 --- a/_code-samples/escrow/js/create-escrow.js +++ /dev/null @@ -1,69 +0,0 @@ -'use strict' -const xrpl = require('xrpl'); -const cc = require('five-bells-condition'); -const crypto = require('crypto'); - -// Useful Documentation:- -// 1. five-bells-condition: https://www.npmjs.com/package/five-bells-condition -// 2. Crypto module: https://nodejs.org/api/crypto.html - -// Your seed value, for testing purposes you can make one with the faucet: -// https://xrpl.org/resources/dev-tools/xrp-faucets -const seed = "sEd7jfWyNG6J71dEojB3W9YdHp2KCjy"; - -async function main() { - try { - - // Connect ---------------------------------------------------------------- - const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233'); - await client.connect(); - - // Prepare wallet to sign the transaction --------------------------------- - const wallet = await xrpl.Wallet.fromSeed(seed); - console.log("Wallet Address: ", wallet.address); - console.log("Seed: ", seed); - - // Set the escrow finish time --------------------------------------------- - let finishAfter = new Date((new Date().getTime() / 1000) + 120); // 2 minutes from now - finishAfter = new Date(finishAfter * 1000); - console.log("This escrow will finish after: ", finishAfter); - - // Construct condition and fulfillment ------------------------------------ - const preimageData = crypto.randomBytes(32); - const myFulfillment = new cc.PreimageSha256(); - myFulfillment.setPreimage(preimageData); - const conditionHex = myFulfillment.getConditionBinary().toString('hex').toUpperCase(); - - console.log('Condition:', conditionHex); - console.log('Fulfillment:', myFulfillment.serializeBinary().toString('hex').toUpperCase()); - - // Prepare EscrowCreate transaction ------------------------------------ - const escrowCreateTransaction = { - "TransactionType": "EscrowCreate", - "Account": wallet.address, - "Destination": wallet.address, - "Amount": "6000000", //drops XRP - "DestinationTag": 2023, - "Condition": conditionHex, // Omit this for time-held escrows - "Fee": "12", - "FinishAfter": xrpl.isoTimeToRippleTime(finishAfter.toISOString()), - }; - - xrpl.validate(escrowCreateTransaction); - - // Sign and submit the transaction ---------------------------------------- - console.log('Signing and submitting the transaction:', - JSON.stringify(escrowCreateTransaction, null, "\t"), "\n" - ); - const response = await client.submitAndWait(escrowCreateTransaction, { wallet }); - console.log(`Sequence number: ${response.result.tx_json.Sequence}`); - console.log(`Finished submitting! ${JSON.stringify(response.result, null, "\t")}`); - - await client.disconnect(); - - } catch (error) { - console.log(error); - } -} - -main() diff --git a/_code-samples/escrow/js/package.json b/_code-samples/escrow/js/package.json index f10d9adf373..7b7cf3a9472 100644 --- a/_code-samples/escrow/js/package.json +++ b/_code-samples/escrow/js/package.json @@ -1,9 +1,10 @@ { "name": "escrow-examples", - "version": "0.0.3", + "version": "2.0.0", "license": "MIT", "dependencies": { "five-bells-condition": "*", - "xrpl": "^4.0.0" - } + "xrpl": "^4.4.0" + }, + "type": "module" } diff --git a/_code-samples/escrow/js/send-timed-escrow-functions.js b/_code-samples/escrow/js/send-timed-escrow-functions.js new file mode 100644 index 00000000000..980c53f2005 --- /dev/null +++ b/_code-samples/escrow/js/send-timed-escrow-functions.js @@ -0,0 +1,146 @@ +import xrpl from 'xrpl' + +/* Sleep function that can be used with await */ +function sleep (delayInSeconds) { + const delayInMs = delayInSeconds * 1000 + return new Promise((resolve) => setTimeout(resolve, delayInMs)) +} + +/* Main function when called as a commandline script */ +async function main () { + const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233') + await client.connect() + + console.log('Funding new wallet from faucet...') + const { wallet } = await client.fundWallet() + const dest_address = 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe' // Testnet faucet + const delay = 30 + + const { escrowSeq, finishAfterRippleTime } = await send_timed_escrow( + client, + wallet, + dest_address, + delay + ) + + await wait_for_escrow(client, finishAfterRippleTime) + + await finish_escrow(client, wallet, escrowSeq) + + client.disconnect() +} + +/* + * Create a time-based escrow. + * Parameters: + * client (xrpl.Client): network-connected client + * wallet (xrpl.Wallet): sender wallet + * dest_address (string): receiver address in base58 + * delay (int): number of seconds until the escrow is mature + * Returns: object with the following keys + * response (xrpl.TxResponse): transaction result from submitAndWait + * escrowSeq (int): sequence number of the created escrow (int) + * finishAfterRippleTime (int): the FinishAfter time of the created escrow, + * in seconds since the Ripple Epoch + */ +async function send_timed_escrow (client, wallet, dest_address, delay) { + // Set the escrow finish time ----------------------------------------------- + const finishAfter = new Date() + finishAfter.setSeconds(finishAfter.getSeconds() + delay) + console.log('This escrow will finish after:', finishAfter) + // Convert finishAfter to seconds since the Ripple Epoch: + const finishAfterRippleTime = xrpl.isoTimeToRippleTime(finishAfter.toISOString()) + + // Send EscrowCreate transaction -------------------------------------------- + const escrowCreate = { + TransactionType: 'EscrowCreate', + Account: wallet.address, + Destination: dest_address, + Amount: '12345', // drops of XRP + FinishAfter: finishAfterRippleTime + } + xrpl.validate(escrowCreate) + + console.log('Signing and submitting the transaction:', + JSON.stringify(escrowCreate, null, 2)) + const response = await client.submitAndWait(escrowCreate, { + wallet, + autofill: true + }) + console.log(JSON.stringify(response.result, null, 2)) + const escrowSeq = response.result.tx_json.Sequence + console.log(`Escrow sequence is ${escrowSeq}.`) + return { + response, + escrowSeq, + finishAfterRippleTime + } +} + +/* + * Check the ledger close time to see if an escrow can be finished. + * If it's not ready yet, wait a number of seconds equal to the difference + * from the latest ledger close time to the escrow's FinishAfter time. + * Parameters: + * client (xrpl.Client): network-connected client + * finishAfterRippleTime (int): the FinishAfter time of the escrow, + * in seconds since the Ripple Epoch + * Returns: null + */ +async function wait_for_escrow (client, finishAfterRippleTime) { + // Check if escrow can be finished ------------------------------------------- + let escrowReady = false + while (!escrowReady) { + // Check the close time of the latest validated ledger. + // Close times are rounded by about 10 seconds, so the exact time the escrow + // is ready to finish may vary by +/- 10 seconds. + const validatedLedger = await client.request({ + command: 'ledger', + ledger_index: 'validated' + }) + const ledgerCloseTime = validatedLedger.result.ledger.close_time + console.log('Latest validated ledger closed at', + xrpl.rippleTimeToISOTime(ledgerCloseTime)) + if (ledgerCloseTime > finishAfterRippleTime) { + escrowReady = true + console.log('Escrow is ready to be finished.') + } else { + let timeDifference = finishAfterRippleTime - ledgerCloseTime + if (timeDifference === 0) { timeDifference = 1 } + console.log(`Waiting another ${timeDifference} second(s).`) + await sleep(timeDifference) + } + } +} + +/* + * Finish an escrow that your account owns. + * Parameters: + * client (xrpl.Client): network-connected client + * wallet (xrpl.Wallet): escrow owner and transaction sender's wallet + * escrowSeq (int): the Sequence number of the escrow to finish + * Returns: null + */ +async function finish_escrow (client, wallet, escrowSeq) { + // Send EscrowFinish transaction -------------------------------------------- + const escrowFinish = { + TransactionType: 'EscrowFinish', + Account: wallet.address, + Owner: wallet.address, + OfferSequence: escrowSeq + } + xrpl.validate(escrowFinish) + + console.log('Signing and submitting the transaction:', + JSON.stringify(escrowFinish, null, 2)) + const response2 = await client.submitAndWait(escrowFinish, { + wallet, + autofill: true + }) + console.log(JSON.stringify(response2.result, null, 2)) + if (response2.result.meta.TransactionResult === 'tesSUCCESS') { + console.log('Escrow finished successfully.') + } +} + +main() diff --git a/_code-samples/escrow/js/send-timed-escrow-linear.js b/_code-samples/escrow/js/send-timed-escrow-linear.js new file mode 100644 index 00000000000..c8079fd37ce --- /dev/null +++ b/_code-samples/escrow/js/send-timed-escrow-linear.js @@ -0,0 +1,92 @@ +import xrpl from 'xrpl' + +/* Sleep function that can be used with await */ +function sleep (delayInSeconds) { + const delayInMs = delayInSeconds * 1000 + return new Promise((resolve) => setTimeout(resolve, delayInMs)) +} + +const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233') +await client.connect() + +console.log('Funding new wallet from faucet...') +const { wallet } = await client.fundWallet() +const dest_address = 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe' // Testnet faucet +const delay = 30 + +// Set the escrow finish time ----------------------------------------------- +const finishAfter = new Date() +finishAfter.setSeconds(finishAfter.getSeconds() + delay) +console.log('This escrow will finish after:', finishAfter) +// Convert finishAfter to seconds since the Ripple Epoch: +const finishAfterRippleTime = xrpl.isoTimeToRippleTime(finishAfter.toISOString()) + +// Send EscrowCreate transaction -------------------------------------------- +const escrowCreate = { + TransactionType: 'EscrowCreate', + Account: wallet.address, + Destination: dest_address, + Amount: '12345', // drops of XRP + FinishAfter: finishAfterRippleTime +} +xrpl.validate(escrowCreate) + +console.log('Signing and submitting the transaction:', + JSON.stringify(escrowCreate, null, 2)) +const response = await client.submitAndWait(escrowCreate, { + wallet, + autofill: true +}) +console.log(JSON.stringify(response.result, null, 2)) +const escrowSeq = response.result.tx_json.Sequence +console.log(`Escrow sequence is ${escrowSeq}.`) + +// Wait for the escrow to be finishable ------------------------------------- +console.log(`Waiting ${delay} seconds for the escrow to mature...`) +await sleep(delay) + +// Check if escrow can be finished ------------------------------------------- +let escrowReady = false +while (!escrowReady) { + // Check the close time of the latest validated ledger. + // Close times are rounded by about 10 seconds, so the exact time the escrow + // is ready to finish may vary by +/- 10 seconds. + const validatedLedger = await client.request({ + command: 'ledger', + ledger_index: 'validated' + }) + const ledgerCloseTime = validatedLedger.result.ledger.close_time + console.log('Latest validated ledger closed at', + xrpl.rippleTimeToISOTime(ledgerCloseTime)) + if (ledgerCloseTime > finishAfterRippleTime) { + escrowReady = true + console.log('Escrow is ready to be finished.') + } else { + let timeDifference = finishAfterRippleTime - ledgerCloseTime + if (timeDifference === 0) { timeDifference = 1 } + console.log(`Waiting another ${timeDifference} second(s).`) + await sleep(timeDifference) + } +} + +// Send EscrowFinish transaction -------------------------------------------- +const escrowFinish = { + TransactionType: 'EscrowFinish', + Account: wallet.address, + Owner: wallet.address, + OfferSequence: escrowSeq +} +xrpl.validate(escrowFinish) + +console.log('Signing and submitting the transaction:', + JSON.stringify(escrowFinish, null, 2)) +const response2 = await client.submitAndWait(escrowFinish, { + wallet, + autofill: true +}) +console.log(JSON.stringify(response2.result, null, 2)) +if (response2.result.meta.TransactionResult === 'tesSUCCESS') { + console.log('Escrow finished successfully.') +} + +client.disconnect() From 8c25a5ea3397acb985226ad32b45a51511aeb79b Mon Sep 17 00:00:00 2001 From: mDuo13 Date: Wed, 17 Sep 2025 18:19:53 -0700 Subject: [PATCH 02/12] Start implementing tutorial for updated timed escrow sample --- .../escrow/js/send-timed-escrow-linear.js | 92 ------------------- ...crow-functions.js => send-timed-escrow.js} | 30 +++--- ...-held-escrow.md => send-a-timed-escrow.md} | 56 +++++++++-- 3 files changed, 68 insertions(+), 110 deletions(-) delete mode 100644 _code-samples/escrow/js/send-timed-escrow-linear.js rename _code-samples/escrow/js/{send-timed-escrow-functions.js => send-timed-escrow.js} (93%) rename docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/{send-a-time-held-escrow.md => send-a-timed-escrow.md} (81%) diff --git a/_code-samples/escrow/js/send-timed-escrow-linear.js b/_code-samples/escrow/js/send-timed-escrow-linear.js deleted file mode 100644 index c8079fd37ce..00000000000 --- a/_code-samples/escrow/js/send-timed-escrow-linear.js +++ /dev/null @@ -1,92 +0,0 @@ -import xrpl from 'xrpl' - -/* Sleep function that can be used with await */ -function sleep (delayInSeconds) { - const delayInMs = delayInSeconds * 1000 - return new Promise((resolve) => setTimeout(resolve, delayInMs)) -} - -const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233') -await client.connect() - -console.log('Funding new wallet from faucet...') -const { wallet } = await client.fundWallet() -const dest_address = 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe' // Testnet faucet -const delay = 30 - -// Set the escrow finish time ----------------------------------------------- -const finishAfter = new Date() -finishAfter.setSeconds(finishAfter.getSeconds() + delay) -console.log('This escrow will finish after:', finishAfter) -// Convert finishAfter to seconds since the Ripple Epoch: -const finishAfterRippleTime = xrpl.isoTimeToRippleTime(finishAfter.toISOString()) - -// Send EscrowCreate transaction -------------------------------------------- -const escrowCreate = { - TransactionType: 'EscrowCreate', - Account: wallet.address, - Destination: dest_address, - Amount: '12345', // drops of XRP - FinishAfter: finishAfterRippleTime -} -xrpl.validate(escrowCreate) - -console.log('Signing and submitting the transaction:', - JSON.stringify(escrowCreate, null, 2)) -const response = await client.submitAndWait(escrowCreate, { - wallet, - autofill: true -}) -console.log(JSON.stringify(response.result, null, 2)) -const escrowSeq = response.result.tx_json.Sequence -console.log(`Escrow sequence is ${escrowSeq}.`) - -// Wait for the escrow to be finishable ------------------------------------- -console.log(`Waiting ${delay} seconds for the escrow to mature...`) -await sleep(delay) - -// Check if escrow can be finished ------------------------------------------- -let escrowReady = false -while (!escrowReady) { - // Check the close time of the latest validated ledger. - // Close times are rounded by about 10 seconds, so the exact time the escrow - // is ready to finish may vary by +/- 10 seconds. - const validatedLedger = await client.request({ - command: 'ledger', - ledger_index: 'validated' - }) - const ledgerCloseTime = validatedLedger.result.ledger.close_time - console.log('Latest validated ledger closed at', - xrpl.rippleTimeToISOTime(ledgerCloseTime)) - if (ledgerCloseTime > finishAfterRippleTime) { - escrowReady = true - console.log('Escrow is ready to be finished.') - } else { - let timeDifference = finishAfterRippleTime - ledgerCloseTime - if (timeDifference === 0) { timeDifference = 1 } - console.log(`Waiting another ${timeDifference} second(s).`) - await sleep(timeDifference) - } -} - -// Send EscrowFinish transaction -------------------------------------------- -const escrowFinish = { - TransactionType: 'EscrowFinish', - Account: wallet.address, - Owner: wallet.address, - OfferSequence: escrowSeq -} -xrpl.validate(escrowFinish) - -console.log('Signing and submitting the transaction:', - JSON.stringify(escrowFinish, null, 2)) -const response2 = await client.submitAndWait(escrowFinish, { - wallet, - autofill: true -}) -console.log(JSON.stringify(response2.result, null, 2)) -if (response2.result.meta.TransactionResult === 'tesSUCCESS') { - console.log('Escrow finished successfully.') -} - -client.disconnect() diff --git a/_code-samples/escrow/js/send-timed-escrow-functions.js b/_code-samples/escrow/js/send-timed-escrow.js similarity index 93% rename from _code-samples/escrow/js/send-timed-escrow-functions.js rename to _code-samples/escrow/js/send-timed-escrow.js index 980c53f2005..dc302d9d002 100644 --- a/_code-samples/escrow/js/send-timed-escrow-functions.js +++ b/_code-samples/escrow/js/send-timed-escrow.js @@ -1,11 +1,5 @@ import xrpl from 'xrpl' -/* Sleep function that can be used with await */ -function sleep (delayInSeconds) { - const delayInMs = delayInSeconds * 1000 - return new Promise((resolve) => setTimeout(resolve, delayInMs)) -} - /* Main function when called as a commandline script */ async function main () { const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233') @@ -13,13 +7,17 @@ async function main () { console.log('Funding new wallet from faucet...') const { wallet } = await client.fundWallet() + + // Define properties of the escrow const dest_address = 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe' // Testnet faucet - const delay = 30 + const delay = 30 // how long to escrow the funds, in seconds + const amount = '12345' // drops of XRP to send in the escrow const { escrowSeq, finishAfterRippleTime } = await send_timed_escrow( client, wallet, dest_address, + amount, delay ) @@ -30,12 +28,13 @@ async function main () { client.disconnect() } -/* +/* send_timed_escrow * Create a time-based escrow. * Parameters: * client (xrpl.Client): network-connected client * wallet (xrpl.Wallet): sender wallet * dest_address (string): receiver address in base58 + * amount (string): how many drops of XRP to send in escrow * delay (int): number of seconds until the escrow is mature * Returns: object with the following keys * response (xrpl.TxResponse): transaction result from submitAndWait @@ -43,7 +42,7 @@ async function main () { * finishAfterRippleTime (int): the FinishAfter time of the created escrow, * in seconds since the Ripple Epoch */ -async function send_timed_escrow (client, wallet, dest_address, delay) { +async function send_timed_escrow (client, wallet, dest_address, amount, delay) { // Set the escrow finish time ----------------------------------------------- const finishAfter = new Date() finishAfter.setSeconds(finishAfter.getSeconds() + delay) @@ -56,7 +55,7 @@ async function send_timed_escrow (client, wallet, dest_address, delay) { TransactionType: 'EscrowCreate', Account: wallet.address, Destination: dest_address, - Amount: '12345', // drops of XRP + Amount: amount, FinishAfter: finishAfterRippleTime } xrpl.validate(escrowCreate) @@ -77,7 +76,7 @@ async function send_timed_escrow (client, wallet, dest_address, delay) { } } -/* +/* wait_for_escrow * Check the ledger close time to see if an escrow can be finished. * If it's not ready yet, wait a number of seconds equal to the difference * from the latest ledger close time to the escrow's FinishAfter time. @@ -113,7 +112,13 @@ async function wait_for_escrow (client, finishAfterRippleTime) { } } -/* +/* Sleep function that can be used with await */ +function sleep (delayInSeconds) { + const delayInMs = delayInSeconds * 1000 + return new Promise((resolve) => setTimeout(resolve, delayInMs)) +} + +/* finish_escrow * Finish an escrow that your account owns. * Parameters: * client (xrpl.Client): network-connected client @@ -143,4 +148,5 @@ async function finish_escrow (client, wallet, escrowSeq) { } } +// Call main function so it runs as a script main() diff --git a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-time-held-escrow.md b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md similarity index 81% rename from docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-time-held-escrow.md rename to docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md index 3f82e9c66e9..f72d7bf4560 100644 --- a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-time-held-escrow.md +++ b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md @@ -1,15 +1,59 @@ --- -html: send-a-time-held-escrow.html -parent: use-escrows.html seo: - description: Create an escrow whose only condition for release is that a specific time has passed. + description: Send an escrow whose only condition for release is that a specific time has passed. labels: - Escrow - - Smart Contracts --- -# Send a Time-Held Escrow +# Send a Timed Escrow + +This tutorial demonstrates how to send an [escrow](../../../../concepts/payment-types/escrow.md) whose only condition for release is that a specific time has passed. You can use this to set aside money for yourself or others so that it absolutely cannot be used until the specified time. + +This tutorial shows how to escrow XRP. If the [TokenEscrow amendment][] is enabled, you can also escrow tokens. + +## Goals + +By following this tutorial, you should learn how to: + +- Convert a timestamp into the XRP Ledger's native format. +- Create and finish an escrow. + +## Prerequisites + +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. + +## Source Code + +You can find the complete source code for this tutorial's examples in the {% repo-link path="_code-samples/escrow/send-timed-escrow.js" %}code samples section of this website's repository{% /repo-link %}. + +## Steps + +### 1. Install dependencies + +{% tabs %} +{% tab label="JavaScript" %} +From the code sample folder, use npm to install dependencies: + +```sh +npm i +``` +{% /tab %} +{% /tabs %} + +### 2. Import dependencies and get accounts + +After importing the XRPL client library, the tutorial code gets a new wallet from the testnet faucet and defines the properties of the escrow as hard-coded constants. You can use an existing wallet or modify the constants as desired. + + +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" before="/* send_timed_escrow" /%} +{% /tab %} +{% /tabs %} + -The [EscrowCreate transaction][] type can create an escrow whose only condition for release is that a specific time has passed. To do this, use the `FinishAfter` field and omit the `Condition` field. ## 1. Calculate release time From 892f9202c5a9cb53e064ca8c381020d253a568a8 Mon Sep 17 00:00:00 2001 From: mDuo13 Date: Fri, 10 Oct 2025 16:38:04 -0700 Subject: [PATCH 03/12] Finish updating timed escrow tutorial --- _code-samples/escrow/js/send-timed-escrow.js | 24 ++- .../use-escrows/send-a-timed-escrow.md | 185 +++--------------- sidebars.yaml | 2 +- 3 files changed, 47 insertions(+), 164 deletions(-) diff --git a/_code-samples/escrow/js/send-timed-escrow.js b/_code-samples/escrow/js/send-timed-escrow.js index dc302d9d002..640d5cdc20a 100644 --- a/_code-samples/escrow/js/send-timed-escrow.js +++ b/_code-samples/escrow/js/send-timed-escrow.js @@ -1,6 +1,7 @@ import xrpl from 'xrpl' -/* Main function when called as a commandline script */ +/* Main function when called as a commandline script ------------------------*/ +main() async function main () { const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233') await client.connect() @@ -43,14 +44,14 @@ async function main () { * in seconds since the Ripple Epoch */ async function send_timed_escrow (client, wallet, dest_address, amount, delay) { - // Set the escrow finish time ----------------------------------------------- + // Set the escrow finish time const finishAfter = new Date() finishAfter.setSeconds(finishAfter.getSeconds() + delay) console.log('This escrow will finish after:', finishAfter) // Convert finishAfter to seconds since the Ripple Epoch: const finishAfterRippleTime = xrpl.isoTimeToRippleTime(finishAfter.toISOString()) - // Send EscrowCreate transaction -------------------------------------------- + // Construct the EscrowCreate transaction const escrowCreate = { TransactionType: 'EscrowCreate', Account: wallet.address, @@ -60,12 +61,15 @@ async function send_timed_escrow (client, wallet, dest_address, amount, delay) { } xrpl.validate(escrowCreate) + // Send the transaction console.log('Signing and submitting the transaction:', JSON.stringify(escrowCreate, null, 2)) const response = await client.submitAndWait(escrowCreate, { wallet, autofill: true }) + + // Display the transaction results & return them console.log(JSON.stringify(response.result, null, 2)) const escrowSeq = response.result.tx_json.Sequence console.log(`Escrow sequence is ${escrowSeq}.`) @@ -76,7 +80,7 @@ async function send_timed_escrow (client, wallet, dest_address, amount, delay) { } } -/* wait_for_escrow +/* wait_for_escrow ------------------------------------------------------------ * Check the ledger close time to see if an escrow can be finished. * If it's not ready yet, wait a number of seconds equal to the difference * from the latest ledger close time to the escrow's FinishAfter time. @@ -87,7 +91,7 @@ async function send_timed_escrow (client, wallet, dest_address, amount, delay) { * Returns: null */ async function wait_for_escrow (client, finishAfterRippleTime) { - // Check if escrow can be finished ------------------------------------------- + // Check if escrow can be finished let escrowReady = false while (!escrowReady) { // Check the close time of the latest validated ledger. @@ -118,7 +122,7 @@ function sleep (delayInSeconds) { return new Promise((resolve) => setTimeout(resolve, delayInMs)) } -/* finish_escrow +/* finish_escrow -------------------------------------------------------------- * Finish an escrow that your account owns. * Parameters: * client (xrpl.Client): network-connected client @@ -127,7 +131,7 @@ function sleep (delayInSeconds) { * Returns: null */ async function finish_escrow (client, wallet, escrowSeq) { - // Send EscrowFinish transaction -------------------------------------------- + // Construct the EscrowFinish transaction const escrowFinish = { TransactionType: 'EscrowFinish', Account: wallet.address, @@ -136,17 +140,17 @@ async function finish_escrow (client, wallet, escrowSeq) { } xrpl.validate(escrowFinish) + // Send the transaction console.log('Signing and submitting the transaction:', JSON.stringify(escrowFinish, null, 2)) const response2 = await client.submitAndWait(escrowFinish, { wallet, autofill: true }) + + // Display the transaction results console.log(JSON.stringify(response2.result, null, 2)) if (response2.result.meta.TransactionResult === 'tesSUCCESS') { console.log('Escrow finished successfully.') } } - -// Call main function so it runs as a script -main() diff --git a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md index f72d7bf4560..c2071ce25c2 100644 --- a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md +++ b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md @@ -42,10 +42,14 @@ npm i {% /tab %} {% /tabs %} -### 2. Import dependencies and get accounts +### 2. Import dependencies and define main function -After importing the XRPL client library, the tutorial code gets a new wallet from the testnet faucet and defines the properties of the escrow as hard-coded constants. You can use an existing wallet or modify the constants as desired. +After importing the XRPL client library, the tutorial code defines the main function which controls the flow of the script. This function does several things: +1. Connect to the network and get a new wallet from the testnet faucet. +2. Define the properties of the escrow as hard-coded constants. +3. Use helper functions to create the escrow, wait for it to be ready, and then finish it. These functions are defined later in the file. +4. Disconnect from the network when done. {% tabs %} {% tab label="JavaScript" %} @@ -53,181 +57,56 @@ After importing the XRPL client library, the tutorial code gets a new wallet fro {% /tab %} {% /tabs %} +### 3. Create the escrow +Next, the `send_timed_escrow(...)` function implements the following: -## 1. Calculate release time - -You must specify the time as whole **[seconds since the Ripple Epoch][]**, which is 946684800 seconds after the UNIX epoch. For example, to release funds at midnight UTC on November 13, 2017: +1. Calculate the maturity time of the escrow (when it should be possible to finish it), and convert it to the correct format ([seconds since the Ripple Epoch][]). + {% admonition type="danger" name="Warning" %}If you use a UNIX time in the `FinishAfter` field without converting to the equivalent Ripple time first, that sets the unlock time to an extra **30 years** in the future!{% /admonition %} +2. Construct an [EscrowCreate transaction][]. + {% admonition type="info" name="Note" %}If you are sending a token escrow, you must also add an expiration time in the `CancelAfter` field, in the same time format. This time must be after the maturity time.{% /admonition %} +3. Submit the transaction to the network and wait for it to be validated by consensus. +4. Return the details of the escrow, particularly the autofilled sequence number. You need this sequence number to identify the escrow in later transactions. {% tabs %} - {% tab label="JavaScript" %} -```js -// JavaScript Date() is natively expressed in milliseconds; convert to seconds -const release_date_unix = Math.floor( new Date("2017-11-13T00:00:00Z") / 1000 ); -const release_date_ripple = release_date_unix - 946684800; -console.log(release_date_ripple); -// 563846400 -``` -{% /tab %} - -{% tab label="Python 3" %} -```python -import datetime -release_date_utc = datetime.datetime(2017,11,13,0,0,0,tzinfo=datetime.timezone.utc) -release_date_ripple = int(release_date_utc.timestamp()) - 946684800 -print(release_date_ripple) -# 563846400 -``` -{% /tab %} - -{% /tabs %} - -{% admonition type="danger" name="Warning" %}If you use a UNIX time in the `FinishAfter` field without converting to the equivalent Ripple time first, that sets the unlock time to an extra **30 years** in the future!{% /admonition %} - -## 2. Submit EscrowCreate transaction - -[Sign and submit](../../../../concepts/transactions/index.md#signing-and-submitting-transactions) an [EscrowCreate transaction][]. Set the `FinishAfter` field of the transaction to the time when the held payment should be released. Omit the `Condition` field to make time the only condition for releasing the held payment. Set the `Destination` to the recipient, which may be the same address as the sender. Set the `Amount` to the total amount of [XRP, in drops][], to escrow. - -{% partial file="/docs/_snippets/secret-key-warning.md" /%} - - -{% tabs %} - -{% tab label="Websocket" %} -Request: -{% code-snippet file="/_api-examples/escrow/websocket/submit-request-escrowcreate-time.json" language="json" /%} -Response: -{% code-snippet file="/_api-examples/escrow/websocket/submit-response-escrowcreate-time.json" language="json" /%} -{% /tab %} - -{% tab label="Javascript" %} -{% code-snippet file="/_code-samples/escrow/js/create-escrow.js" language="js" from="// Prepare EscrowCreate" before="await client.disconnect" /%} -{% /tab %} - -{% tab label="Python" %} -{% code-snippet file="/_code-samples/escrow/py/create_escrow.py" language="py" from="# Build escrow create" /%} -{% /tab %} - -{% /tabs %} - -Take note of the transaction's identifying `hash` value so you can check its final status when it is included in a validated ledger version. - -## 3. Wait for validation - -{% raw-partial file="/docs/_snippets/wait-for-validation.md" /%} - -## 4. Confirm that the escrow was created - -Use the [tx method][] with the transaction's identifying hash to check its final status. Look for a `CreatedNode` in the transaction metadata to indicate that it created an [Escrow ledger object](../../../../concepts/payment-types/escrow.md). - -Request: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/tx-request-escrowcreate-time.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -Response: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/tx-response-escrowcreate-time.json" language="json" /%} +{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="/* send_timed_escrow" before="/* wait_for_escrow" /%} {% /tab %} - {% /tabs %} -## 5. Wait for the release time - -Held payments with a `FinishAfter` time cannot be finished until a ledger has already closed with a [`close_time` header field](../../../../references/protocol/ledger-data/ledger-header.md) that is later than the Escrow node's `FinishAfter` time. - -You can check the close time of the most recently-validated ledger with the [ledger method][]: - -Request: -{% tabs %} +### 4. Wait for the escrow -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/ledger-request.json" language="json" /%} -{% /tab %} +The `wait_for_escrow(...)` function implements the following: -{% /tabs %} - -Response: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/ledger-response.json" language="json" /%} -{% /tab %} - -{% /tabs %} - - -## 6. Submit EscrowFinish transaction - -[Sign and submit](../../../../concepts/transactions/index.md#signing-and-submitting-transactions) an [EscrowFinish transaction][] to execute the release of the funds after the `FinishAfter` time has passed. Set the `Owner` field of the transaction to the `Account` address from the EscrowCreate transaction, and the `OfferSequence` to the `Sequence` number from the EscrowCreate transaction. For an escrow held only by time, omit the `Condition` and `Fulfillment` fields. - -{% admonition type="success" name="Tip" %} -The EscrowFinish transaction is necessary because the XRP Ledger's state can only be modified by transactions. The sender of this transaction may be the recipient of the escrow, the original sender of the escrow, or any other XRP Ledger address. -{% /admonition %} - -If the escrow has expired, you can only [cancel the escrow](cancel-an-expired-escrow.md) instead. - -{% partial file="/docs/_snippets/secret-key-warning.md" /%} +1. Check the official close time of the most recent validated ledger. +2. Wait a number of seconds based on the difference between that close time and the time when the escrow is ready to be finished. +3. Repeat until the escrow is ready. The actual, official close time of ledgers [is rounded](../../../../concepts/ledgers/ledger-close-times.md) by up to 10 seconds, so there is some variance in how long it actually takes for an escrow to be ready. {% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="/* wait_for_escrow" before="/* Sleep function" /%} -{% tab label="Websocket" %} -Request: -{% code-snippet file="/_api-examples/escrow/websocket/submit-request-escrowfinish-time.json" language="json" /%} +Additionally, since JavaScript doesn't have a native `sleep(...)` function, the sample code implements one to be used with `await`, as a convenience: -Response: -{% code-snippet file="/_api-examples/escrow/websocket/tx-response-escrowfinish-time.json" language="json" /%} -{% /tab %} - -{% tab label="Javascript" %} -{% code-snippet file="/_code-samples/escrow/js/finish-escrow.js" language="js" from="// Prepare EscrowFinish" before="await client.disconnect" /%} -{% /tab %} +{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="/* Sleep function" before="/* finish_escrow" /%} -{% tab label="Python" %} -{% code-snippet file="/_code-samples/escrow/py/finish_escrow.py" language="py" from="# Build escrow finish" /%} {% /tab %} - {% /tabs %} -Take note of the transaction's identifying `hash` value so you can check its final status when it is included in a validated ledger version. - -## 7. Wait for validation - -{% raw-partial file="/docs/_snippets/wait-for-validation.md" /%} +### 5. Finish the escrow -## 8. Confirm final result +The `finish_escrow(...)` function implements the following: -Use the [tx method][] with the EscrowFinish transaction's identifying hash to check its final status. In particular, look in the transaction metadata for a `ModifiedNode` of type `AccountRoot` for the destination of the escrowed payment. The `FinalFields` of the object should show the increase in XRP in the `Balance` field. - -Request: +1. Construct an [EscrowFinish transaction][], using the sequence number recorded when the escrow was created. + {% admonition type="success" name="Tip" %}Anyone can finish a timed escrow when it is ready. Regardless of who does so—the sender, receiver, or even a third party—the escrow delivers the funds to its intended recipient.{% /admonition %} +2. Submit the transaction to the network and wait for it to be validated by consensus. +3. Display the details of the validated transaction. {% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/tx-request-escrowfinish-time.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -Response: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/tx-response-escrowfinish-time.json" language="json" /%} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="/* finish_escrow" /%} {% /tab %} - {% /tabs %} diff --git a/sidebars.yaml b/sidebars.yaml index 7443fb81a06..bb492584222 100644 --- a/sidebars.yaml +++ b/sidebars.yaml @@ -299,7 +299,7 @@ - page: docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/index.md expanded: false items: - - page: docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-time-held-escrow.md + - page: docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md - page: docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md - page: docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/cancel-an-expired-escrow.md - page: docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/look-up-escrows.md From 8f301ed391a8131cf111017e4294086976105ec6 Mon Sep 17 00:00:00 2001 From: mDuo13 Date: Mon, 13 Oct 2025 14:23:07 -0700 Subject: [PATCH 04/12] rm reference to pre-refactor escrow code sample --- .../use-escrows/send-a-conditionally-held-escrow.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md index f2dad19faa1..61cf19c16f0 100644 --- a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md +++ b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md @@ -106,16 +106,14 @@ Response: {% code-snippet file="/_api-examples/escrow/websocket/submit-response-escrowcreate-condition.json" language="json" /%} {% /tab %} -{% tab label="Javascript" %} -{% code-snippet file="/_code-samples/escrow/js/create-escrow.js" language="js" from="// Prepare EscrowCreate" before="await client.disconnect" /%} -{% /tab %} - {% tab label="Python" %} {% code-snippet file="/_code-samples/escrow/py/create_escrow.py" language="py" from="# Build escrow create" /%} {% /tab %} {% /tabs %} + + ## 4. Wait for validation {% raw-partial file="/docs/_snippets/wait-for-validation.md" /%} From 95196d3195cab7d18dd1e3857a57e2ea39e3f2b5 Mon Sep 17 00:00:00 2001 From: mDuo13 Date: Thu, 16 Oct 2025 13:38:13 -0700 Subject: [PATCH 05/12] Make tutorial for linear version of the timed escrow code --- _code-samples/escrow/js/send-timed-escrow.js | 211 ++++++------------ .../use-escrows/send-a-timed-escrow.md | 76 ++++--- 2 files changed, 118 insertions(+), 169 deletions(-) diff --git a/_code-samples/escrow/js/send-timed-escrow.js b/_code-samples/escrow/js/send-timed-escrow.js index 640d5cdc20a..5b0b98c8924 100644 --- a/_code-samples/escrow/js/send-timed-escrow.js +++ b/_code-samples/escrow/js/send-timed-escrow.js @@ -1,120 +1,44 @@ import xrpl from 'xrpl' -/* Main function when called as a commandline script ------------------------*/ -main() -async function main () { - const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233') - await client.connect() +const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233') +await client.connect() - console.log('Funding new wallet from faucet...') - const { wallet } = await client.fundWallet() +console.log('Funding new wallet from faucet...') +const { wallet } = await client.fundWallet() - // Define properties of the escrow - const dest_address = 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe' // Testnet faucet - const delay = 30 // how long to escrow the funds, in seconds - const amount = '12345' // drops of XRP to send in the escrow +// Set the escrow finish time ----------------------------------------------- +const delay = 30 // Seconds in the future when the escrow should mature +const finishAfter = new Date() // Current time +finishAfter.setSeconds(finishAfter.getSeconds() + delay) +console.log('This escrow will finish after:', finishAfter) +// Convert finishAfter to seconds since the Ripple Epoch: +const finishAfterRippleTime = xrpl.isoTimeToRippleTime(finishAfter.toISOString()) - const { escrowSeq, finishAfterRippleTime } = await send_timed_escrow( - client, - wallet, - dest_address, - amount, - delay - ) - - await wait_for_escrow(client, finishAfterRippleTime) - - await finish_escrow(client, wallet, escrowSeq) - - client.disconnect() +// Send EscrowCreate transaction -------------------------------------------- +const escrowCreate = { + TransactionType: 'EscrowCreate', + Account: wallet.address, + Destination: 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe', // Testnet faucet + Amount: '123456', // drops of XRP + FinishAfter: finishAfterRippleTime } +xrpl.validate(escrowCreate) -/* send_timed_escrow - * Create a time-based escrow. - * Parameters: - * client (xrpl.Client): network-connected client - * wallet (xrpl.Wallet): sender wallet - * dest_address (string): receiver address in base58 - * amount (string): how many drops of XRP to send in escrow - * delay (int): number of seconds until the escrow is mature - * Returns: object with the following keys - * response (xrpl.TxResponse): transaction result from submitAndWait - * escrowSeq (int): sequence number of the created escrow (int) - * finishAfterRippleTime (int): the FinishAfter time of the created escrow, - * in seconds since the Ripple Epoch - */ -async function send_timed_escrow (client, wallet, dest_address, amount, delay) { - // Set the escrow finish time - const finishAfter = new Date() - finishAfter.setSeconds(finishAfter.getSeconds() + delay) - console.log('This escrow will finish after:', finishAfter) - // Convert finishAfter to seconds since the Ripple Epoch: - const finishAfterRippleTime = xrpl.isoTimeToRippleTime(finishAfter.toISOString()) +console.log('Signing and submitting the transaction:', + JSON.stringify(escrowCreate, null, 2)) +const response = await client.submitAndWait(escrowCreate, { + wallet, + autofill: true +}) +console.log(JSON.stringify(response.result, null, 2)) - // Construct the EscrowCreate transaction - const escrowCreate = { - TransactionType: 'EscrowCreate', - Account: wallet.address, - Destination: dest_address, - Amount: amount, - FinishAfter: finishAfterRippleTime - } - xrpl.validate(escrowCreate) +// Save the sequence number so you can identify the escrow later. +const escrowSeq = response.result.tx_json.Sequence +console.log(`Escrow sequence is ${escrowSeq}.`) - // Send the transaction - console.log('Signing and submitting the transaction:', - JSON.stringify(escrowCreate, null, 2)) - const response = await client.submitAndWait(escrowCreate, { - wallet, - autofill: true - }) - - // Display the transaction results & return them - console.log(JSON.stringify(response.result, null, 2)) - const escrowSeq = response.result.tx_json.Sequence - console.log(`Escrow sequence is ${escrowSeq}.`) - return { - response, - escrowSeq, - finishAfterRippleTime - } -} - -/* wait_for_escrow ------------------------------------------------------------ - * Check the ledger close time to see if an escrow can be finished. - * If it's not ready yet, wait a number of seconds equal to the difference - * from the latest ledger close time to the escrow's FinishAfter time. - * Parameters: - * client (xrpl.Client): network-connected client - * finishAfterRippleTime (int): the FinishAfter time of the escrow, - * in seconds since the Ripple Epoch - * Returns: null - */ -async function wait_for_escrow (client, finishAfterRippleTime) { - // Check if escrow can be finished - let escrowReady = false - while (!escrowReady) { - // Check the close time of the latest validated ledger. - // Close times are rounded by about 10 seconds, so the exact time the escrow - // is ready to finish may vary by +/- 10 seconds. - const validatedLedger = await client.request({ - command: 'ledger', - ledger_index: 'validated' - }) - const ledgerCloseTime = validatedLedger.result.ledger.close_time - console.log('Latest validated ledger closed at', - xrpl.rippleTimeToISOTime(ledgerCloseTime)) - if (ledgerCloseTime > finishAfterRippleTime) { - escrowReady = true - console.log('Escrow is ready to be finished.') - } else { - let timeDifference = finishAfterRippleTime - ledgerCloseTime - if (timeDifference === 0) { timeDifference = 1 } - console.log(`Waiting another ${timeDifference} second(s).`) - await sleep(timeDifference) - } - } -} +// Wait for the escrow to be finishable ------------------------------------- +console.log(`Waiting ${delay} seconds for the escrow to mature...`) +await sleep(delay) /* Sleep function that can be used with await */ function sleep (delayInSeconds) { @@ -122,35 +46,48 @@ function sleep (delayInSeconds) { return new Promise((resolve) => setTimeout(resolve, delayInMs)) } -/* finish_escrow -------------------------------------------------------------- - * Finish an escrow that your account owns. - * Parameters: - * client (xrpl.Client): network-connected client - * wallet (xrpl.Wallet): escrow owner and transaction sender's wallet - * escrowSeq (int): the Sequence number of the escrow to finish - * Returns: null - */ -async function finish_escrow (client, wallet, escrowSeq) { - // Construct the EscrowFinish transaction - const escrowFinish = { - TransactionType: 'EscrowFinish', - Account: wallet.address, - Owner: wallet.address, - OfferSequence: escrowSeq - } - xrpl.validate(escrowFinish) - - // Send the transaction - console.log('Signing and submitting the transaction:', - JSON.stringify(escrowFinish, null, 2)) - const response2 = await client.submitAndWait(escrowFinish, { - wallet, - autofill: true +// Check if escrow can be finished ------------------------------------------- +let escrowReady = false +while (!escrowReady) { + // Check the close time of the latest validated ledger. + // Close times are rounded by about 10 seconds, so the exact time the escrow + // is ready to finish may vary by +/- 10 seconds. + const validatedLedger = await client.request({ + command: 'ledger', + ledger_index: 'validated' }) - - // Display the transaction results - console.log(JSON.stringify(response2.result, null, 2)) - if (response2.result.meta.TransactionResult === 'tesSUCCESS') { - console.log('Escrow finished successfully.') + const ledgerCloseTime = validatedLedger.result.ledger.close_time + console.log('Latest validated ledger closed at', + xrpl.rippleTimeToISOTime(ledgerCloseTime)) + if (ledgerCloseTime > finishAfterRippleTime) { + escrowReady = true + console.log('Escrow is mature.') + } else { + let timeDifference = finishAfterRippleTime - ledgerCloseTime + if (timeDifference === 0) { timeDifference = 1 } + console.log(`Waiting another ${timeDifference} second(s).`) + await sleep(timeDifference) } } + +// Send EscrowFinish transaction -------------------------------------------- +const escrowFinish = { + TransactionType: 'EscrowFinish', + Account: wallet.address, + Owner: wallet.address, + OfferSequence: escrowSeq +} +xrpl.validate(escrowFinish) + +console.log('Signing and submitting the transaction:', + JSON.stringify(escrowFinish, null, 2)) +const response2 = await client.submitAndWait(escrowFinish, { + wallet, + autofill: true +}) +console.log(JSON.stringify(response2.result, null, 2)) +if (response2.result.meta.TransactionResult === 'tesSUCCESS') { + console.log('Escrow finished successfully.') +} + +client.disconnect() diff --git a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md index c2071ce25c2..9f476153dae 100644 --- a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md +++ b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md @@ -42,70 +42,82 @@ npm i {% /tab %} {% /tabs %} -### 2. Import dependencies and define main function +### 2. Set up client and account -After importing the XRPL client library, the tutorial code defines the main function which controls the flow of the script. This function does several things: +To get started, import the client library and instantiate an API client. For this tutorial, you also need one account, which you can get from the faucet. -1. Connect to the network and get a new wallet from the testnet faucet. -2. Define the properties of the escrow as hard-coded constants. -3. Use helper functions to create the escrow, wait for it to be ready, and then finish it. These functions are defined later in the file. -4. Disconnect from the network when done. +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" before="// Set the escrow finish time" /%} +{% /tab %} +{% /tabs %} + +### 3. Calculate the finish time + +To make a timed escrow, you need to set the maturity time of the escrow, which is a timestamp after which the escrow can be finished, formatted as [seconds since the Ripple Epoch][]. You can calculate the maturity time by adding a delay to the current time and then using the client library's conversion function. The sample code uses a delay of 30 seconds: {% tabs %} {% tab label="JavaScript" %} -{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" before="/* send_timed_escrow" /%} +{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="// Set the escrow finish time" before="// Send EscrowCreate transaction" /%} {% /tab %} {% /tabs %} -### 3. Create the escrow +{% admonition type="danger" name="Warning" %}If you use a UNIX time without converting to the equivalent Ripple time first, that sets the maturity time to an extra **30 years** in the future!{% /admonition %} + +If you want your escrow to have an expiration time, after which it can only be canceled, you can calculate it the same way. -Next, the `send_timed_escrow(...)` function implements the following: +### 4. Create the escrow -1. Calculate the maturity time of the escrow (when it should be possible to finish it), and convert it to the correct format ([seconds since the Ripple Epoch][]). - {% admonition type="danger" name="Warning" %}If you use a UNIX time in the `FinishAfter` field without converting to the equivalent Ripple time first, that sets the unlock time to an extra **30 years** in the future!{% /admonition %} -2. Construct an [EscrowCreate transaction][]. - {% admonition type="info" name="Note" %}If you are sending a token escrow, you must also add an expiration time in the `CancelAfter` field, in the same time format. This time must be after the maturity time.{% /admonition %} -3. Submit the transaction to the network and wait for it to be validated by consensus. -4. Return the details of the escrow, particularly the autofilled sequence number. You need this sequence number to identify the escrow in later transactions. +To send the escrow, construct an [EscrowCreate transaction][] and then submit it to the network. The fields of this transaction define the properties of the escrow. The sample code uses hard-coded values to send 0.123456 XRP back to the Testnet faucet: {% tabs %} {% tab label="JavaScript" %} -{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="/* send_timed_escrow" before="/* wait_for_escrow" /%} +{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="// Send EscrowCreate transaction" before="// Save the sequence number" /%} + +{% admonition type="info" name="Note" %}To give the escrow an expiration time, add a `CancelAfter` field to the transaction. An expiration time is optional for timed XRP escrows but required for token escrows. This time must be after the maturity time.{% /admonition %} + +Save the sequence number of the EscrowCreate transaction. (In this example, the sequence number is autofilled.) You need this sequence number to identify the escrow when you want to finish (or cancel) it later. + +{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="// Save the sequence number" before="// Wait for the escrow" /%} {% /tab %} {% /tabs %} -### 4. Wait for the escrow +### 5. Wait for the escrow -The `wait_for_escrow(...)` function implements the following: - -1. Check the official close time of the most recent validated ledger. -2. Wait a number of seconds based on the difference between that close time and the time when the escrow is ready to be finished. -3. Repeat until the escrow is ready. The actual, official close time of ledgers [is rounded](../../../../concepts/ledgers/ledger-close-times.md) by up to 10 seconds, so there is some variance in how long it actually takes for an escrow to be ready. +With the escrow successfully created, the funds are now locked up until the maturity time. Since this tutorial used a delay of 30 seconds, have the script sleep for that long: {% tabs %} {% tab label="JavaScript" %} -{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="/* wait_for_escrow" before="/* Sleep function" /%} +{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="// Wait for the escrow" before="/* Sleep function" /%} + +JavaScript doesn't have a native `sleep(...)` function, but you can implement one to be used with `await`, as a convenience: -Additionally, since JavaScript doesn't have a native `sleep(...)` function, the sample code implements one to be used with `await`, as a convenience: +{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="/* Sleep function" before="// Check if escrow can be finished" /%} +{% /tab %} +{% /tabs %} -{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="/* Sleep function" before="/* finish_escrow" /%} +At this point, the escrow should be mature, but that depends on the official close time of the previous ledger. Ledger close times can vary based on the consensus process, and [are rounded](../../../../concepts/ledgers/ledger-close-times.md) by up to 10 seconds. To account for this variance, use an approach such as the following: +1. Check the official close time of the most recent validated ledger. +2. Wait a number of seconds based on the difference between that close time and the maturity time of the escrow. +3. Repeat until the escrow is mature. + +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="// Check if escrow can be finished" before="// Send EscrowFinish transaction" /%} {% /tab %} {% /tabs %} -### 5. Finish the escrow +### 6. Finish the escrow -The `finish_escrow(...)` function implements the following: +Now that the escrow is mature, you can finish it. Construct an [EscrowFinish transaction][], using the sequence number that you recorded when you created the escrow, then submit it to the network. -1. Construct an [EscrowFinish transaction][], using the sequence number recorded when the escrow was created. - {% admonition type="success" name="Tip" %}Anyone can finish a timed escrow when it is ready. Regardless of who does so—the sender, receiver, or even a third party—the escrow delivers the funds to its intended recipient.{% /admonition %} -2. Submit the transaction to the network and wait for it to be validated by consensus. -3. Display the details of the validated transaction. +{% admonition type="success" name="Tip" %}Anyone can finish a timed escrow when it is ready. Regardless of who does so—the sender, receiver, or even a third party—the escrow delivers the funds to its intended recipient.{% /admonition %} {% tabs %} {% tab label="JavaScript" %} -{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="/* finish_escrow" /%} +{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="// Send EscrowFinish transaction" /%} {% /tab %} {% /tabs %} From 847b0f71b94db31424fdf8b22e683c25e56a20a6 Mon Sep 17 00:00:00 2001 From: mDuo13 Date: Fri, 14 Nov 2025 16:24:46 -0800 Subject: [PATCH 06/12] Escrow tutorials: Add conditional example & workaround for faucet tecDIR_FULL --- .../escrow/js/send-conditional-escrow.js | 89 +++++++++++++++++++ _code-samples/escrow/js/send-timed-escrow.js | 13 ++- 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 _code-samples/escrow/js/send-conditional-escrow.js diff --git a/_code-samples/escrow/js/send-conditional-escrow.js b/_code-samples/escrow/js/send-conditional-escrow.js new file mode 100644 index 00000000000..594b4d303b8 --- /dev/null +++ b/_code-samples/escrow/js/send-conditional-escrow.js @@ -0,0 +1,89 @@ +import xrpl from 'xrpl' +import { PreimageSha256 } from 'five-bells-condition' +import { randomBytes } from 'crypto' + +const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233') +await client.connect() + +console.log('Funding new wallet from faucet...') +const { wallet } = await client.fundWallet() +const destination_address = 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe' // Testnet faucet +// Alternative: Get another account to send the escrow to. Use this if you get +// a tecDIR_FULL error trying to create escrows to the Testnet faucet. +// const destination_address = (await client.fundWallet()).wallet.address + +// Create the crypto-condition for release ---------------------------------- +const preimage = randomBytes(32) +const fulfillment = new PreimageSha256() +fulfillment.setPreimage(preimage) +const fulfillmentHex = fulfillment.serializeBinary().toString('hex').toUpperCase() +const conditionHex = fulfillment.getConditionBinary().toString('hex').toUpperCase() +console.log('Condition:', conditionHex) +console.log('Fulfillment:', fulfillmentHex) + +// Set the escrow expiration ------------------------------------------------ +const cancelDelay = 300 // Seconds in the future when the escrow should expire +const cancelAfter = new Date() // Current time +cancelAfter.setSeconds(cancelAfter.getSeconds() + cancelDelay) +console.log('This escrow will expire after:', cancelAfter) +// Convert cancelAfter to seconds since the Ripple Epoch: +const cancelAfterRippleTime = xrpl.isoTimeToRippleTime(cancelAfter.toISOString()) + +// Send EscrowCreate transaction -------------------------------------------- +const escrowCreate = { + TransactionType: 'EscrowCreate', + Account: wallet.address, + Destination: destination_address, + Amount: '123456', // drops of XRP + Condition: conditionHex, + CancelAfter: cancelAfterRippleTime +} +xrpl.validate(escrowCreate) + +console.log('Signing and submitting the transaction:', + JSON.stringify(escrowCreate, null, 2)) +const response = await client.submitAndWait(escrowCreate, { + wallet, + autofill: true // Note: fee is higher based on condition size in bytes +}) + +// Check result of submitting ----------------------------------------------- +console.log(JSON.stringify(response.result, null, 2)) +const escrowCreateResultCode = response.result.meta.TransactionResult +if (escrowCreateResultCode === 'tesSUCCESS') { + console.log('Escrow created successfully.') +} else { + console.error(`EscrowCreate failed with code ${escrowCreateResultCode}.`) + process.exit(1) +} + +// Save the sequence number so you can identify the escrow later. +const escrowSeq = response.result.tx_json.Sequence +console.log(`Escrow sequence is ${escrowSeq}.`) + + +// Send EscrowFinish transaction -------------------------------------------- +const escrowFinish = { + TransactionType: 'EscrowFinish', + Account: wallet.address, + Owner: wallet.address, + OfferSequence: escrowSeq, + Condition: conditionHex, + Fulfillment: fulfillmentHex +} +xrpl.validate(escrowFinish) + +console.log('Signing and submitting the transaction:', + JSON.stringify(escrowFinish, null, 2)) +const response2 = await client.submitAndWait(escrowFinish, { + wallet, + autofill: true // Note: fee is higher based on fulfillment size in bytes +}) +console.log(JSON.stringify(response2.result, null, 2)) +if (response2.result.meta.TransactionResult === 'tesSUCCESS') { + console.log('Escrow finished successfully.') +} else { + console.log(`Failed with result code ${response2.result.meta.TransactionResult}`) +} + +client.disconnect() diff --git a/_code-samples/escrow/js/send-timed-escrow.js b/_code-samples/escrow/js/send-timed-escrow.js index 5b0b98c8924..723d026741b 100644 --- a/_code-samples/escrow/js/send-timed-escrow.js +++ b/_code-samples/escrow/js/send-timed-escrow.js @@ -5,6 +5,10 @@ await client.connect() console.log('Funding new wallet from faucet...') const { wallet } = await client.fundWallet() +const destination_address = 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe' // Testnet faucet +// Alternative: Get another account to send the escrow to. Use this if you get +// a tecDIR_FULL error trying to create escrows to the Testnet faucet. +// const destination_address = (await client.fundWallet()).wallet.address // Set the escrow finish time ----------------------------------------------- const delay = 30 // Seconds in the future when the escrow should mature @@ -18,7 +22,7 @@ const finishAfterRippleTime = xrpl.isoTimeToRippleTime(finishAfter.toISOString() const escrowCreate = { TransactionType: 'EscrowCreate', Account: wallet.address, - Destination: 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe', // Testnet faucet + Destination: destination_address, Amount: '123456', // drops of XRP FinishAfter: finishAfterRippleTime } @@ -31,6 +35,13 @@ const response = await client.submitAndWait(escrowCreate, { autofill: true }) console.log(JSON.stringify(response.result, null, 2)) +const escrowCreateResultCode = response.result.meta.TransactionResult +if (escrowCreateResultCode === 'tesSUCCESS') { + console.log('Escrow created successfully.') +} else { + console.error(`EscrowCreate failed with code ${escrowCreateResultCode}.`) + process.exit(1) +} // Save the sequence number so you can identify the escrow later. const escrowSeq = response.result.tx_json.Sequence From f26008d2bab3a0d1169455c174d93c4050d2e7f0 Mon Sep 17 00:00:00 2001 From: mDuo13 Date: Wed, 3 Dec 2025 20:26:22 -0800 Subject: [PATCH 07/12] Rewrite Python escrow tutorials in new format --- _code-samples/escrow/py/create_escrow.py | 52 ----------- _code-samples/escrow/py/requirements.txt | 2 +- .../escrow/py/send_conditional_escrow.py | 74 ++++++++++++++++ _code-samples/escrow/py/send_timed_escrow.py | 86 +++++++++++++++++++ 4 files changed, 161 insertions(+), 53 deletions(-) delete mode 100644 _code-samples/escrow/py/create_escrow.py create mode 100644 _code-samples/escrow/py/send_conditional_escrow.py create mode 100644 _code-samples/escrow/py/send_timed_escrow.py diff --git a/_code-samples/escrow/py/create_escrow.py b/_code-samples/escrow/py/create_escrow.py deleted file mode 100644 index ecd75df9a29..00000000000 --- a/_code-samples/escrow/py/create_escrow.py +++ /dev/null @@ -1,52 +0,0 @@ -from datetime import datetime, timedelta - -from xrpl.clients import JsonRpcClient -from xrpl.models import EscrowCreate -from xrpl.transaction import submit_and_wait -from xrpl.utils import datetime_to_ripple_time, xrp_to_drops -from xrpl.wallet import generate_faucet_wallet - -# Create Escrow - -client = JsonRpcClient("https://s.altnet.rippletest.net:51234") # Connect to client - -amount_to_escrow = 10.000 - -receiver_addr = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe" # Example: send back to Testnet Faucet - -# Escrow will be available to claim after 3 days -claim_date = datetime_to_ripple_time(datetime.now() + timedelta(days=3)) - -# Escrow will expire after 5 days -expiry_date = datetime_to_ripple_time(datetime.now() + timedelta(days=5)) - -# Optional field -# You can optionally use a Crypto Condition to allow for dynamic release of funds. For example: -condition = "A02580205A0E9E4018BE1A6E0F51D39B483122EFDF1DDEF3A4BE83BE71522F9E8CDAB179810120" # do not use in production - -# sender wallet object -sender_wallet = generate_faucet_wallet(client=client) - -# Build escrow create transaction -create_txn = EscrowCreate( - account=sender_wallet.address, - amount=xrp_to_drops(amount_to_escrow), - destination=receiver_addr, - finish_after=claim_date, - cancel_after=expiry_date, - condition=condition # Omit this for time-held escrows -) - -# Autofill, sign, then submit transaction and wait for result -stxn_response = submit_and_wait(create_txn, client, sender_wallet) - -# Return result of transaction -stxn_result = stxn_response.result - - -# Parse result and print out the neccesary info -print(stxn_result["tx_json"]["Account"]) -print(stxn_result["tx_json"]["Sequence"]) - -print(stxn_result["meta"]["TransactionResult"]) -print(stxn_result["hash"]) diff --git a/_code-samples/escrow/py/requirements.txt b/_code-samples/escrow/py/requirements.txt index 617e07eb84f..cbb2826eb7f 100644 --- a/_code-samples/escrow/py/requirements.txt +++ b/_code-samples/escrow/py/requirements.txt @@ -1,2 +1,2 @@ xrpl-py>=3.0.0 -cryptoconditions +cryptoconditions==0.8.1 diff --git a/_code-samples/escrow/py/send_conditional_escrow.py b/_code-samples/escrow/py/send_conditional_escrow.py new file mode 100644 index 00000000000..8e33387c690 --- /dev/null +++ b/_code-samples/escrow/py/send_conditional_escrow.py @@ -0,0 +1,74 @@ +import json +from datetime import datetime, timedelta +from os import urandom + +from cryptoconditions import PreimageSha256 +from xrpl.clients import JsonRpcClient +from xrpl.models import EscrowCreate, EscrowFinish +from xrpl.transaction import submit_and_wait +from xrpl.utils import datetime_to_ripple_time +from xrpl.wallet import generate_faucet_wallet + +# Set up client and get a wallet +client = JsonRpcClient("https://s.altnet.rippletest.net:51234") +print("Funding new wallet from faucet...") +wallet = generate_faucet_wallet(client, debug=True) +destination_address = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe" # Testnet faucet +# Alternative: Get another account to send the escrow to. Use this if you get +# a tecDIR_FULL error trying to create escrows to the Testnet faucet. +# destination_address = generate_faucet_wallet(client, debug=True).address + +# Create the crypto-condition for release ----------------------------------- +preimage = urandom(32) +fulfillment = PreimageSha256(preimage=preimage) +condition_hex = fulfillment.condition_binary.hex().upper() +fulfillment_hex = fulfillment.serialize_binary().hex().upper() +print("Condition:", condition_hex) +print("Fulfillment:", fulfillment_hex) + +# Set the escrow expiration ------------------------------------------------- +cancel_delay = 300 +cancel_after = datetime.now() + timedelta(seconds=cancel_delay) +print("This escrow will expire after", cancel_after) +cancel_after_rippletime = datetime_to_ripple_time(cancel_after) + +# Send EscrowCreate transaction --------------------------------------------- +escrow_create = EscrowCreate( + account=wallet.address, + destination=destination_address, + amount="123456", # drops of XRP + condition=condition_hex, + cancel_after=cancel_after_rippletime +) + +print("Signing and submitting the EscrowCreate transaction.") +response = submit_and_wait(escrow_create, client, wallet, autofill=True) +print(json.dumps(response.result, indent=2)) + +# Check result of submitting ------------------------------------------------ +result_code = response.result["meta"]["TransactionResult"] +if result_code != "tesSUCCESS": + print(f"EscrowCreate failed with result code {result_code}") + exit(1) + +# Save the sequence number so you can identify the escrow later +escrow_seq = response.result["tx_json"]["Sequence"] + +# Send the EscrowFinish transaction ----------------------------------------- +escrow_finish = EscrowFinish( + account=wallet.address, + owner=wallet.address, + offer_sequence=escrow_seq, + condition=condition_hex, + fulfillment=fulfillment_hex +) +print("Signing and submitting the EscrowFinish transaction.") +response2 = submit_and_wait(escrow_finish, client, wallet, autofill=True) +print(json.dumps(response2.result, indent=2)) + +result_code = response2.result["meta"]["TransactionResult"] +if result_code != "tesSUCCESS": + print(f"EscrowFinish failed with result code {result_code}") + exit(1) + +print("Escrow finished successfully.") diff --git a/_code-samples/escrow/py/send_timed_escrow.py b/_code-samples/escrow/py/send_timed_escrow.py new file mode 100644 index 00000000000..2d164f2f580 --- /dev/null +++ b/_code-samples/escrow/py/send_timed_escrow.py @@ -0,0 +1,86 @@ +import json +from datetime import datetime, timedelta +from time import sleep +from os import urandom + +from xrpl.clients import JsonRpcClient +from xrpl.models import EscrowCreate, EscrowFinish +from xrpl.models.requests import Ledger +from xrpl.transaction import submit_and_wait +from xrpl.utils import datetime_to_ripple_time, ripple_time_to_datetime +from xrpl.wallet import generate_faucet_wallet + +# Set up client and get a wallet +client = JsonRpcClient("https://s.altnet.rippletest.net:51234") +print("Funding new wallet from faucet...") +wallet = generate_faucet_wallet(client, debug=True) +# destination_address = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe" # Testnet faucet +# Alternative: Get another account to send the escrow to. Use this if you get +# a tecDIR_FULL error trying to create escrows to the Testnet faucet. +destination_address = generate_faucet_wallet(client, debug=True).address + +# Set the escrow finish time ------------------------------------------------ +delay = 30 +finish_after = datetime.now() + timedelta(seconds=delay) +print("This escrow will expire after", finish_after) +finish_after_rippletime = datetime_to_ripple_time(finish_after) + +# Send EscrowCreate transaction --------------------------------------------- +escrow_create = EscrowCreate( + account=wallet.address, + destination=destination_address, + amount="123456", # drops of XRP + finish_after=finish_after_rippletime +) + +print("Signing and submitting the EscrowCreate transaction.") +response = submit_and_wait(escrow_create, client, wallet, autofill=True) +print(json.dumps(response.result, indent=2)) + +# Check result of submitting ------------------------------------------------ +result_code = response.result["meta"]["TransactionResult"] +if result_code != "tesSUCCESS": + print(f"EscrowCreate failed with result code {result_code}") + exit(1) + +# Save the sequence number so you can identify the escrow later +escrow_seq = response.result["tx_json"]["Sequence"] + +# Wait for the escrow to be finishable -------------------------------------- +sleep(delay) + +# Check if escrow can be finished ------------------------------------------- +escrow_ready = False +while not escrow_ready: + validated_ledger = client.request(Ledger(ledger_index="validated")) + ledger_close_time = validated_ledger.result["ledger"]["close_time"] + print("Latest validated ledger closed at", + ripple_time_to_datetime(ledger_close_time) + ) + if ledger_close_time > finish_after_rippletime: + escrow_ready = True + print("Escrow is mature.") + else: + time_difference = finish_after_rippletime - ledger_close_time + if time_difference == 0: + time_difference = 1 + print(f"Waiting another {time_difference} seconds.") + sleep(time_difference) + + +# Send the EscrowFinish transaction ----------------------------------------- +escrow_finish = EscrowFinish( + account=wallet.address, + owner=wallet.address, + offer_sequence=escrow_seq +) +print("Signing and submitting the EscrowFinish transaction.") +response2 = submit_and_wait(escrow_finish, client, wallet, autofill=True) +print(json.dumps(response2.result, indent=2)) + +result_code = response2.result["meta"]["TransactionResult"] +if result_code != "tesSUCCESS": + print(f"EscrowFinish failed with result code {result_code}") + exit(1) + +print("Escrow finished successfully.") From ebd1fd2c6a3bf402137b4c8fc22688bc66b392dd Mon Sep 17 00:00:00 2001 From: mDuo13 Date: Thu, 4 Dec 2025 15:31:57 -0800 Subject: [PATCH 08/12] Remove some old Escrow tutorials and rewrite conditional escrow tutorial in new format --- .../send-a-conditionally-held-escrow.md | 173 -------------- .../use-escrows/send-a-time-held-escrow.md | 188 --------------- .../use-cases/payments/smart-contracts-uc.md | 2 +- .../escrow/websocket/ledger-request.json | 5 - .../escrow/websocket/ledger-response.json | 28 --- ...submit-request-escrowcreate-condition.json | 13 -- .../submit-request-escrowcreate-time.json | 12 - ...submit-request-escrowfinish-condition.json | 14 -- .../submit-request-escrowfinish-time.json | 11 - ...ubmit-response-escrowcreate-condition.json | 25 -- .../submit-response-escrowcreate-time.json | 24 -- ...ubmit-response-escrowfinish-condition.json | 25 -- .../submit-response-escrowfinish-time.json | 23 -- .../tx-request-escrowcreate-condition.json | 5 - .../tx-request-escrowcreate-time.json | 5 - .../tx-request-escrowfinish-condition.json | 5 - .../tx-request-escrowfinish-time.json | 5 - .../tx-response-escrowcreate-condition.json | 81 ------- .../tx-response-escrowcreate-time.json | 78 ------- .../tx-response-escrowfinish-condition.json | 95 -------- .../tx-response-escrowfinish-time.json | 92 -------- _code-samples/escrow/py/generate_condition.py | 19 -- .../escrow/py/send_conditional_escrow.py | 4 +- _code-samples/escrow/py/send_timed_escrow.py | 10 +- .../use-escrows/send-a-conditional-escrow.md | 140 ++++++++++++ .../send-a-conditionally-held-escrow.md | 214 ------------------ .../use-escrows/send-a-timed-escrow.md | 53 ++++- .../use-an-escrow-as-a-smart-contract.md | 156 ------------- docs/use-cases/payments/smart-contracts-uc.md | 4 +- redirects.yaml | 8 +- sidebars.yaml | 3 +- 31 files changed, 199 insertions(+), 1321 deletions(-) delete mode 100644 @l10n/ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md delete mode 100644 @l10n/ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-time-held-escrow.md delete mode 100644 _api-examples/escrow/websocket/ledger-request.json delete mode 100644 _api-examples/escrow/websocket/ledger-response.json delete mode 100644 _api-examples/escrow/websocket/submit-request-escrowcreate-condition.json delete mode 100644 _api-examples/escrow/websocket/submit-request-escrowcreate-time.json delete mode 100644 _api-examples/escrow/websocket/submit-request-escrowfinish-condition.json delete mode 100644 _api-examples/escrow/websocket/submit-request-escrowfinish-time.json delete mode 100644 _api-examples/escrow/websocket/submit-response-escrowcreate-condition.json delete mode 100644 _api-examples/escrow/websocket/submit-response-escrowcreate-time.json delete mode 100644 _api-examples/escrow/websocket/submit-response-escrowfinish-condition.json delete mode 100644 _api-examples/escrow/websocket/submit-response-escrowfinish-time.json delete mode 100644 _api-examples/escrow/websocket/tx-request-escrowcreate-condition.json delete mode 100644 _api-examples/escrow/websocket/tx-request-escrowcreate-time.json delete mode 100644 _api-examples/escrow/websocket/tx-request-escrowfinish-condition.json delete mode 100644 _api-examples/escrow/websocket/tx-request-escrowfinish-time.json delete mode 100644 _api-examples/escrow/websocket/tx-response-escrowcreate-condition.json delete mode 100644 _api-examples/escrow/websocket/tx-response-escrowcreate-time.json delete mode 100644 _api-examples/escrow/websocket/tx-response-escrowfinish-condition.json delete mode 100644 _api-examples/escrow/websocket/tx-response-escrowfinish-time.json delete mode 100644 _code-samples/escrow/py/generate_condition.py create mode 100644 docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow.md delete mode 100644 docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md delete mode 100644 docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/use-an-escrow-as-a-smart-contract.md diff --git a/@l10n/ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md b/@l10n/ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md deleted file mode 100644 index eadd5853fab..00000000000 --- a/@l10n/ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -html: send-a-conditionally-held-escrow.html -parent: use-escrows.html -seo: - description: 満たされた条件に基づいてリリースとなるEscrowを作成します。 -labels: - - Escrow - - スマートコントラクト ---- -# 条件に基づくEscrowの送信 - -## 1.条件とフルフィルメントの生成 - -XRP Ledger EscrowにはPREIMAGE-SHA-256 [Crypto-Conditions](https://tools.ietf.org/html/draft-thomas-crypto-conditions-03)が必要です。条件とフルフィルメントを適切なフォーマットで計算するには、[five-bells-condition](https://github.com/interledgerjs/five-bells-condition)などのCrypto-conditionsライブラリを使用する必要があります。フルフィルメントについては、以下のフルフィルメントを生成するためのメソッドのいずれかを使用することが推奨されます。 - -- 暗号論的に安全な乱数ソースを使用して、32バイト以上のランダムバイトを生成します。 -- Interledger Protocolの[PSK仕様](https://github.com/interledger/rfcs/blob/master/deprecated/0016-pre-shared-key/0016-pre-shared-key.md)に従い、ILPパケットのHMAC-SHA-256をフルフィルメントとして使用します。 - -ランダムなフルフィルメントと条件のJavaScriptコードの例: - -```js -const cc = require('five-bells-condition') -const crypto = require('crypto') - -const preimageData = crypto.randomBytes(32) -const myFulfillment = new cc.PreimageSha256() -myFulfillment.setPreimage(preimageData) - -const condition = myFulfillment.getConditionBinary().toString('hex').toUpperCase() -console.log('Condition:', condition) -// (Random hexadecimal, 72 chars in length) - -// keep secret until you want to finish executing the held payment: -const fulfillment = myFulfillment.serializeBinary().toString('hex').toUpperCase() -console.log('Fulfillment:', fulfillment) -// (Random hexadecimal, 78 chars in length) -``` - -後で使用できるように条件とフルフィルメントを保存します。保留中の支払いの実行が完了するまでは、フルフィルメントを公開しないでください。フルフィルメントを知っていれば誰でもEscrowを終了でき、保留中の資金を指定された送金先にリリースできます。 - - -## 2.リリース時刻または取消し時刻の計算 - -条件付き`Escrow`トランザクションには、`CancelAfter`フィールドと`FinishAfter`フィールドのいずれか、または両方が含まれている必要があります。`CancelAfter`フィールドを使用すると、指定の時刻までに条件を満たすことができなかった場合に送金元へXRPを返金できます。`FinishAfter`フィールドに指定される時刻より前の時間は、正しいフルフィルメントが送信されてもEscrowを実行できません。いずれのフィールドでも、将来の時刻を指定する必要があります。 - -`CancelAfter`の時刻を24時間先に設定する例: - -{% tabs %} - -{% tab label="JavaScript" %} -```js -const rippleOffset = 946684800 -const CancelAfter = Math.floor(Date.now() / 1000) + (24*60*60) - rippleOffset -console.log(CancelAfter) -// Example:556927412 -``` -{% /tab %} - -{% tab label="Python 2/3" %} -```python -from time import time -ripple_offset = 946684800 -cancel_after = int(time()) + (24*60*60) - 946684800 -print(cancel_after) -# Example: 556927412 -``` -{% /tab %} - -{% /tabs %} - -{% admonition type="danger" name="警告" %}XRP Ledgerでは、時刻を**Rippleエポック(2000-01-01T00:00:00Z)以降の経過秒数**として指定する必要があります。`CancelAfter`または`FinishAfter`フィールドで、UNIX時刻を同等のRipple時刻に変換せずに使用すると、ロック解除時刻が**30年**先に設定されることになります。{% /admonition %} - -## 3.EscrowCreateトランザクションの送信 - -[EscrowCreateトランザクション][]に[署名して送信](../../../../concepts/transactions/index.md#トランザクションへの署名とトランザクションの送信)します。トランザクションの`Condition`フィールドを、保留中の支払いがリリースされる時刻に設定します。`Destination`を受取人に設定します。受取人と送金元のアドレスは同じでもかまいません。前の手順で算出した`CancelAfter`または`FinishAfter`の時刻も指定します。`Amount`を、Escrowする[XRPのdrop数][]の合計額に設定します。 - -{% partial file="/@l10n/ja/docs/_snippets/secret-key-warning.md" /%} - -リクエスト: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/submit-request-escrowcreate-condition.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -レスポンス: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/submit-response-escrowcreate-condition.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -## 4.検証の待機 - -{% partial file="/@l10n/ja/docs/_snippets/wait-for-validation.md" /%} - -## 5.Escrowが作成されたことの確認 - -トランザクションの識別用ハッシュを指定した[txメソッド][]を使用して、トランザクションの最終ステータスを確認します。特に、[Escrowレジャーオブジェクト](../../../../concepts/payment-types/escrow.md)が作成されたことを示す`CreatedNode`をトランザクションメタデータで探します。 - -リクエスト: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/tx-request-escrowcreate-condition.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -レスポンス: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/tx-response-escrowcreate-condition.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -## 6.EscrowFinishトランザクションの送信 - -`FinishAfter`の時刻が経過した後で資金のリリースを実行する[EscrowFinishトランザクション][]に[署名して送信](../../../../concepts/transactions/index.md#トランザクションへの署名とトランザクションの送信)します。トランザクションの`Owner`フィールドにEscrowCreateトランザクションの`Account`アドレスを設定し、`OfferSequence` にEscrowCreateトランザクションの`Sequence`番号を設定します。`Condition`フィールドと`Fulfillment`フィールドに、ステップ1で生成した条件値とフルフィルメント値をそれぞれ16進数で設定します。フルフィルメントのサイズ(バイト数)に基づいて`Fee`([トランザクションコスト](../../../../concepts/transactions/transaction-cost.md))の値を設定します。条件付きEscrowFinishでは、少なくとも330 drop(XRP)と、フルフィルメントのサイズで16バイトごとに10 dropが必要です。 - -{% admonition type="info" name="注記" %}EscrowCreateトランザクションに`FinishAfter`フィールドが含まれている場合、Escrowの条件として正しいフルフィルメントを指定しても、この時刻よりも前の時点ではこのトランザクションを実行できません。前に閉鎖されたレジャーの閉鎖時刻が`FinishAfter`の時刻よりも前である場合、EscrowFinishトランザクションは[結果コード](../../../../references/protocol/transactions/transaction-results/index.md)`tecNO_PERMISSION`で失敗します。{% /admonition %} - -Escrowが有効期限切れの場合は、[Escrowの取消し](cancel-an-expired-escrow.md)だけが可能です。 - -{% partial file="/@l10n/ja/docs/_snippets/secret-key-warning.md" /%} - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/submit-request-escrowfinish-condition.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -レスポンス: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/submit-response-escrowfinish-condition.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -トランザクションの識別用`hash`値をメモしておきます。これにより、検証済みレジャーバージョンに記録されるときにその最終ステータスを確認できます。 - -## 7.検証の待機 - -{% partial file="/@l10n/ja/docs/_snippets/wait-for-validation.md" /%} - -## 8.最終結果の確認 - -EscrowFinishトランザクションの識別用ハッシュを指定した[txメソッド][]を使用して、トランザクションの最終ステータスを確認します。特にトランザクションメタデータ内で、エスクローに預託された支払いの送金先の`ModifiedNode`(タイプが`AccountRoot`)を確認します。オブジェクトの`FinalFields`に、`Balance`フィールドのXRP返金額の増分が表示されている必要があります。 - -リクエスト: - -{% code-snippet file="/_api-examples/escrow/websocket/tx-request-escrowfinish-condition.json" language="json" /%} - -レスポンス: - -{% code-snippet file="/_api-examples/escrow/websocket/tx-response-escrowfinish-condition.json" language="json" /%} - -{% raw-partial file="/@l10n/ja/docs/_snippets/common-links.md" /%} diff --git a/@l10n/ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-time-held-escrow.md b/@l10n/ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-time-held-escrow.md deleted file mode 100644 index 35fa5036251..00000000000 --- a/@l10n/ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-time-held-escrow.md +++ /dev/null @@ -1,188 +0,0 @@ ---- -html: send-a-time-held-escrow.html -parent: use-escrows.html -seo: - description: 指定した時間が経過することがリリースの唯一の条件であるEscrowを作成します。 -labels: - - Escrow - - スマートコントラクト ---- -# 時間に基づくEscrowの送信 - -[EscrowCreateトランザクション][]タイプでは、リリースの唯一の条件が特定時刻を経過することであるEscrowを作成できます。このためには、`FinishAfter`フィールドを使用し、`Condition`フィールドを省略します。 - -## 1.リリース時刻の計算 - -時刻を **[Rippleエポック以降の経過秒数][]** として指定する必要があります。Rippleエポックは、UNIXエポックの946684800秒後です。たとえば、2017年11月13日の午前0時(UTC)に資金をリリースする場合、以下のようになります。 - -{% tabs %} - -{% tab label="JavaScript" %} -```js -// JavaScript Date() is natively expressed in milliseconds; convert to seconds -const release_date_unix = Math.floor( new Date("2017-11-13T00:00:00Z") / 1000 ); -const release_date_ripple = release_date_unix - 946684800; -console.log(release_date_ripple); -// 563846400 -``` -{% /tab %} - -{% tab label="Python 3" %} -```python -import datetime -release_date_utc = datetime.datetime(2017,11,13,0,0,0,tzinfo=datetime.timezone.utc) -release_date_ripple = int(release_date_utc.timestamp()) - 946684800 -print(release_date_ripple) -# 563846400 -``` -{% /tab %} - -{% /tabs %} - -{% admonition type="danger" name="警告" %}`FinishAfter`フィールドで、UNIX時刻を同等のRipple時刻に変換せずに使用すると、ロック解除時刻が30年先に設定されることになります。{% /admonition %} - -## 2.EscrowCreateトランザクションの送信 - -[EscrowCreateトランザクション][]に[署名して送信](../../../../concepts/transactions/index.md#トランザクションへの署名とトランザクションの送信)します。トランザクションの`FinishAfter`フィールドを、保留中の支払いがリリースされる時刻に設定します。`Condition`フィールドを省略して、時刻を保留中の支払いをリリースする唯一の条件とします。`Destination`を受取人に設定します。受取人と送金元のアドレスは同じでもかまいません。`Amount`を、Escrowする[XRPのdrop数][]の合計額に設定します。 - -{% partial file="/@l10n/ja/docs/_snippets/secret-key-warning.md" /%} - -リクエスト: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/submit-request-escrowcreate-time.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -レスポンス: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/submit-response-escrowcreate-time.json" language="json" /%} -{% /tab %} - -{% /tabs %} - - -トランザクションの識別用`hash`値をメモしておきます。これにより、検証済みレジャーバージョンに記録されるときにその最終ステータスを確認できます。 - -## 3.検証の待機 - -{% partial file="/@l10n/ja/docs/_snippets/wait-for-validation.md" /%} - -## 4.Escrowが作成されたことの確認 - -トランザクションの識別用ハッシュを指定した[txメソッド][]を使用して、トランザクションの最終ステータスを確認します。[Escrowレジャーオブジェクト](../../../../concepts/payment-types/escrow.md)が作成されたことを示す`CreatedNode`をトランザクションメタデータで探します。 - -リクエスト: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/tx-request-escrowcreate-time.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -レスポンス: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/tx-response-escrowcreate-time.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -## 5.リリース時刻までの待機 - -`FinishAfter`時刻が指定されている保留中の支払いは、Escrowノードの`FinishAfter`時刻よりも後の[`close_time`ヘッダーフィールド](../../../../references/protocol/ledger-data/ledger-header.md)の時刻でレジャーが閉鎖するまでは完了できません。 - -最新の検証済みレジャーの閉鎖時刻は、[ledgerメソッド][]を使用して検索できます。 - -リクエスト: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/ledger-request.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -レスポンス: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/ledger-response.json" language="json" /%} -{% /tab %} - -{% /tabs %} - - -## 6.EscrowFinishトランザクションの送信 - -`FinishAfter`の時刻が経過した後で資金のリリースを実行する[EscrowFinishトランザクション][]に[署名して送信](../../../../concepts/transactions/index.md#トランザクションへの署名とトランザクションの送信)します。トランザクションの`Owner`フィールドにEscrowCreateトランザクションの`Account`アドレスを設定し、`OfferSequence` にEscrowCreateトランザクションの`Sequence`番号を設定します。時刻のみに基づいて保留されているEscrowの場合は、`Condition`フィールドと`Fulfillment`フィールドを省略します。 - -{% admonition type="success" name="ヒント" %}XRP Ledgerの状態はトランザクションでしか変更できないため、EscrowFinishトランザクションが必要です。このトランザクションの送信者は、Escrowの受取人、Escrowの元としての送金人、またはその他のXRP Ledgerアドレスのいずれかです。{% /admonition %} - -Escrowが有効期限切れの場合は、[Escrowの取消し](cancel-an-expired-escrow.md)だけが可能です。 - -{% partial file="/@l10n/ja/docs/_snippets/secret-key-warning.md" /%} - -リクエスト: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/submit-request-escrowfinish-time.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -レスポンス: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/submit-response-escrowfinish-time.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -トランザクションの識別用`hash`値をメモしておきます。これにより、検証済みレジャーバージョンに記録されるときにその最終ステータスを確認できます。 - -## 7.検証の待機 - -{% partial file="/@l10n/ja/docs/_snippets/wait-for-validation.md" /%} - -## 8.最終結果の確認 - -EscrowFinishトランザクションの識別用ハッシュを指定した[txメソッド][]を使用して、トランザクションの最終ステータスを確認します。特にトランザクションメタデータ内で、エスクローに預託された支払いの送金先の`ModifiedNode`(タイプが`AccountRoot`)を確認します。オブジェクトの`FinalFields`に、`Balance`フィールドのXRP返金額の増分が表示されている必要があります。 - -リクエスト: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/tx-request-escrowfinish-time.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -レスポンス: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/tx-response-escrowfinish-time.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -{% raw-partial file="/@l10n/ja/docs/_snippets/common-links.md" /%} diff --git a/@l10n/ja/docs/use-cases/payments/smart-contracts-uc.md b/@l10n/ja/docs/use-cases/payments/smart-contracts-uc.md index 150203c78a1..8411d55ddb9 100644 --- a/@l10n/ja/docs/use-cases/payments/smart-contracts-uc.md +++ b/@l10n/ja/docs/use-cases/payments/smart-contracts-uc.md @@ -37,7 +37,7 @@ XRP Ledger上のスマートコントラクトは、条件付きで保有する オラクルのプログラムが条件を満たしたことを検知した後、エスクローの受取人にfulfillmentの16進数値を渡します。この時点以降、オラクルはエスクローを終了させるなど、何も行いません。エスクローの受取人は、ほとんどの場合、エスクローを終了することになります。 -[conditionとfulfillmentの生成](../../tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md#1-generate-condition-and-fulfillment)をご覧ください。 +[条件に基づくEscrowの送信](../../tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow.md)をご覧ください。 ## 例 diff --git a/_api-examples/escrow/websocket/ledger-request.json b/_api-examples/escrow/websocket/ledger-request.json deleted file mode 100644 index fe3c8e9f55f..00000000000 --- a/_api-examples/escrow/websocket/ledger-request.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "id": 4, - "command": "ledger", - "ledger_index": "validated" -} diff --git a/_api-examples/escrow/websocket/ledger-response.json b/_api-examples/escrow/websocket/ledger-response.json deleted file mode 100644 index e05a1a6db0f..00000000000 --- a/_api-examples/escrow/websocket/ledger-response.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "id": 4, - "status": "success", - "type": "response", - "result": { - "ledger": { - "accepted": true, - "account_hash": "3B5A8FF5334F94F4D3D09F236F9D1B4C028FCAE30948ACC986D461DDEE1D886B", - "close_flags": 0, - "close_time": 557256670, - "close_time_human": "2017-Aug-28 17:31:10", - "close_time_resolution": 10, - "closed": true, - "hash": "A999223A80174A7CB39D766B625C9E476F24AD2F15860A712CD029EE5ED1C320", - "ledger_hash": "A999223A80174A7CB39D766B625C9E476F24AD2F15860A712CD029EE5ED1C320", - "ledger_index": "1908253", - "parent_close_time": 557256663, - "parent_hash": "6A70C5336ACFDA05760D827776079F7A544D2361CFD5B21BD55A92AA20477A61", - "seqNum": "1908253", - "totalCoins": "99997280690562728", - "total_coins": "99997280690562728", - "transaction_hash": "49A51DFB1CAB2F134D93D5D1C5FF55A15B12DA36DAF9F5862B17C47EE966647D" - }, - "ledger_hash": "A999223A80174A7CB39D766B625C9E476F24AD2F15860A712CD029EE5ED1C320", - "ledger_index": 1908253, - "validated": true - } -} diff --git a/_api-examples/escrow/websocket/submit-request-escrowcreate-condition.json b/_api-examples/escrow/websocket/submit-request-escrowcreate-condition.json deleted file mode 100644 index 2439399d035..00000000000 --- a/_api-examples/escrow/websocket/submit-request-escrowcreate-condition.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "id": 1, - "command": "submit", - "secret": "s████████████████████████████", - "tx_json": { - "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB", - "TransactionType": "EscrowCreate", - "Amount": "100000", - "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", - "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120", - "CancelAfter": 556927412 - } -} diff --git a/_api-examples/escrow/websocket/submit-request-escrowcreate-time.json b/_api-examples/escrow/websocket/submit-request-escrowcreate-time.json deleted file mode 100644 index b85ddd1de5b..00000000000 --- a/_api-examples/escrow/websocket/submit-request-escrowcreate-time.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "id": 2, - "command": "submit", - "secret": "s████████████████████████████", - "tx_json": { - "Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", - "TransactionType": "EscrowCreate", - "Amount": "10000", - "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", - "FinishAfter": 557020800 - } -} diff --git a/_api-examples/escrow/websocket/submit-request-escrowfinish-condition.json b/_api-examples/escrow/websocket/submit-request-escrowfinish-condition.json deleted file mode 100644 index 0a61bcdd9d5..00000000000 --- a/_api-examples/escrow/websocket/submit-request-escrowfinish-condition.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "id": 4, - "command": "submit", - "secret": "s████████████████████████████", - "tx_json": { - "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB", - "TransactionType": "EscrowFinish", - "Owner": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB", - "OfferSequence": 5, - "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120", - "Fulfillment": "A0228020D280D1A02BAD0D2EBC0528B92E9BF37AC3E2530832C2C52620307135156F1048", - "Fee": "500" - } -} diff --git a/_api-examples/escrow/websocket/submit-request-escrowfinish-time.json b/_api-examples/escrow/websocket/submit-request-escrowfinish-time.json deleted file mode 100644 index 422f69f60b2..00000000000 --- a/_api-examples/escrow/websocket/submit-request-escrowfinish-time.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "id": 5, - "command": "submit", - "secret": "s████████████████████████████", - "tx_json": { - "Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", - "TransactionType": "EscrowFinish", - "Owner": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", - "OfferSequence": 1 - } -} diff --git a/_api-examples/escrow/websocket/submit-response-escrowcreate-condition.json b/_api-examples/escrow/websocket/submit-response-escrowcreate-condition.json deleted file mode 100644 index 62f8ae1ccb9..00000000000 --- a/_api-examples/escrow/websocket/submit-response-escrowcreate-condition.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "id": 1, - "status": "success", - "type": "response", - "result": { - "engine_result": "tesSUCCESS", - "engine_result_code": 0, - "engine_result_message": "The transaction was applied. Only final in a validated ledger.", - "tx_blob": "120001228000000024000000052024213209B46140000000000186A068400000000000000A732103E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B61744730450221008AC8BDC2151D5EF956197F0E6E89A4F49DEADC1AC38367870E444B1EA8D88D97022075E31427B455DFF87F0F22B849C71FC3987A91C19D63B6D0242E808347EC8A8F701127A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD81012081149A2AA667E1517EFA8A6B552AB2EDB859A99F26B283144B4E9C06F24296074F7BC48F92A97916C6DC5EA9", - "tx_json": { - "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB", - "Amount": "100000", - "CancelAfter": 556927412, - "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120", - "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", - "Fee": "10", - "Flags": 2147483648, - "Sequence": 5, - "SigningPubKey": "03E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B61", - "TransactionType": "EscrowCreate", - "TxnSignature": "30450221008AC8BDC2151D5EF956197F0E6E89A4F49DEADC1AC38367870E444B1EA8D88D97022075E31427B455DFF87F0F22B849C71FC3987A91C19D63B6D0242E808347EC8A8F", - "hash": "E22D1F6EB006CAD35E0DBD3B4F3748427055E4C143EBE95AA6603823AEEAD324" - } - } -} diff --git a/_api-examples/escrow/websocket/submit-response-escrowcreate-time.json b/_api-examples/escrow/websocket/submit-response-escrowcreate-time.json deleted file mode 100644 index 75570a1423c..00000000000 --- a/_api-examples/escrow/websocket/submit-response-escrowcreate-time.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "id": 2, - "status": "success", - "type": "response", - "result": { - "engine_result": "tesSUCCESS", - "engine_result_code": 0, - "engine_result_message": "The transaction was applied. Only final in a validated ledger.", - "tx_blob": "1200012280000000240000000120252133768061400000000000271068400000000000000A732103C3555B7339FFDDB43495A8371A3A87B4C66B67D49D06CB9BA1FDBFEEB57B6E437446304402203C9AA4C21E1A1A7427D41583283E7A513DDBDD967B246CADD3B2705D858A7A8E02201BEA7B923B18910EEB9F306F6DE3B3F53549BBFAD46335B62B4C34A6DCB4A47681143EEB46C355B04EE8D08E8EED00F422895C79EA6A83144B4E9C06F24296074F7BC48F92A97916C6DC5EA9", - "tx_json": { - "Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", - "Amount": "10000", - "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", - "Fee": "10", - "FinishAfter": 557020800, - "Flags": 2147483648, - "Sequence": 1, - "SigningPubKey": "03C3555B7339FFDDB43495A8371A3A87B4C66B67D49D06CB9BA1FDBFEEB57B6E43", - "TransactionType": "EscrowCreate", - "TxnSignature": "304402203C9AA4C21E1A1A7427D41583283E7A513DDBDD967B246CADD3B2705D858A7A8E02201BEA7B923B18910EEB9F306F6DE3B3F53549BBFAD46335B62B4C34A6DCB4A476", - "hash": "55B2057332F8999208C43BA1E7091B423A16E5ED2736C06300B4076085205263" - } - } -} diff --git a/_api-examples/escrow/websocket/submit-response-escrowfinish-condition.json b/_api-examples/escrow/websocket/submit-response-escrowfinish-condition.json deleted file mode 100644 index 551a30ecd69..00000000000 --- a/_api-examples/escrow/websocket/submit-response-escrowfinish-condition.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "id": 4, - "status": "success", - "type": "response", - "result": { - "engine_result": "tesSUCCESS", - "engine_result_code": 0, - "engine_result_message": "The transaction was applied. Only final in a validated ledger.", - "tx_blob": "120002228000000024000000062019000000056840000000000001F4732103E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B617446304402207DE4EA9C8655E75BA01F96345B3F62074313EB42C15D9C4871E30F02202D2BA50220070E52AD308A31AC71E33BA342F31B68D1D1B2A7A3A3ED6E8552CA3DCF14FBB2701024A0228020D280D1A02BAD0D2EBC0528B92E9BF37AC3E2530832C2C52620307135156F1048701127A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD81012081149A2AA667E1517EFA8A6B552AB2EDB859A99F26B282149A2AA667E1517EFA8A6B552AB2EDB859A99F26B2", - "tx_json": { - "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB", - "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120", - "Fee": "500", - "Flags": 2147483648, - "Fulfillment": "A0228020D280D1A02BAD0D2EBC0528B92E9BF37AC3E2530832C2C52620307135156F1048", - "OfferSequence": 5, - "Owner": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB", - "Sequence": 6, - "SigningPubKey": "03E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B61", - "TransactionType": "EscrowFinish", - "TxnSignature": "304402207DE4EA9C8655E75BA01F96345B3F62074313EB42C15D9C4871E30F02202D2BA50220070E52AD308A31AC71E33BA342F31B68D1D1B2A7A3A3ED6E8552CA3DCF14FBB2", - "hash": "0E88368CAFC69A722ED829FAE6E2DD3575AE9C192691E60B5ACDF706E219B2BF" - } - } -} diff --git a/_api-examples/escrow/websocket/submit-response-escrowfinish-time.json b/_api-examples/escrow/websocket/submit-response-escrowfinish-time.json deleted file mode 100644 index 7204644ef3f..00000000000 --- a/_api-examples/escrow/websocket/submit-response-escrowfinish-time.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "id": 5, - "status": "success", - "type": "response", - "result": { - "engine_result": "tesSUCCESS", - "engine_result_code": 0, - "engine_result_message": "The transaction was applied. Only final in a validated ledger.", - "tx_blob": "1200022280000000240000000220190000000168400000000000000A732103C3555B7339FFDDB43495A8371A3A87B4C66B67D49D06CB9BA1FDBFEEB57B6E4374473045022100923B91BA4FD6450813F5335D71C64BA9EB81304A86859A631F2AD8571424A46502200CCE660D36781B84634C5F23619EB6CFCCF942709F54DCCF27CF6F499AE78C9B81143EEB46C355B04EE8D08E8EED00F422895C79EA6A82143EEB46C355B04EE8D08E8EED00F422895C79EA6A", - "tx_json": { - "Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", - "Fee": "10", - "Flags": 2147483648, - "OfferSequence": 1, - "Owner": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", - "Sequence": 2, - "SigningPubKey": "03C3555B7339FFDDB43495A8371A3A87B4C66B67D49D06CB9BA1FDBFEEB57B6E43", - "TransactionType": "EscrowFinish", - "TxnSignature": "3045022100923B91BA4FD6450813F5335D71C64BA9EB81304A86859A631F2AD8571424A46502200CCE660D36781B84634C5F23619EB6CFCCF942709F54DCCF27CF6F499AE78C9B", - "hash": "41856A742B3CAF307E7B4D0B850F302101F0F415B785454F7501E9960A2A1F6B" - } - } -} diff --git a/_api-examples/escrow/websocket/tx-request-escrowcreate-condition.json b/_api-examples/escrow/websocket/tx-request-escrowcreate-condition.json deleted file mode 100644 index b4de83b0492..00000000000 --- a/_api-examples/escrow/websocket/tx-request-escrowcreate-condition.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "id": 3, - "command": "tx", - "transaction": "E22D1F6EB006CAD35E0DBD3B4F3748427055E4C143EBE95AA6603823AEEAD324" -} diff --git a/_api-examples/escrow/websocket/tx-request-escrowcreate-time.json b/_api-examples/escrow/websocket/tx-request-escrowcreate-time.json deleted file mode 100644 index 171e3741aa2..00000000000 --- a/_api-examples/escrow/websocket/tx-request-escrowcreate-time.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "id": 3, - "command": "tx", - "transaction": "55B2057332F8999208C43BA1E7091B423A16E5ED2736C06300B4076085205263" -} diff --git a/_api-examples/escrow/websocket/tx-request-escrowfinish-condition.json b/_api-examples/escrow/websocket/tx-request-escrowfinish-condition.json deleted file mode 100644 index d830e7e0f40..00000000000 --- a/_api-examples/escrow/websocket/tx-request-escrowfinish-condition.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "id": 20, - "command": "tx", - "transaction": "0E88368CAFC69A722ED829FAE6E2DD3575AE9C192691E60B5ACDF706E219B2BF" -} diff --git a/_api-examples/escrow/websocket/tx-request-escrowfinish-time.json b/_api-examples/escrow/websocket/tx-request-escrowfinish-time.json deleted file mode 100644 index 11b038d6924..00000000000 --- a/_api-examples/escrow/websocket/tx-request-escrowfinish-time.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "id": 21, - "command": "tx", - "transaction": "41856A742B3CAF307E7B4D0B850F302101F0F415B785454F7501E9960A2A1F6B" -} diff --git a/_api-examples/escrow/websocket/tx-response-escrowcreate-condition.json b/_api-examples/escrow/websocket/tx-response-escrowcreate-condition.json deleted file mode 100644 index e61f232edf2..00000000000 --- a/_api-examples/escrow/websocket/tx-response-escrowcreate-condition.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "id": 3, - "status": "success", - "type": "response", - "result": { - "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB", - "Amount": "100000", - "CancelAfter": 556927412, - "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120", - "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", - "Fee": "10", - "Flags": 2147483648, - "Sequence": 5, - "SigningPubKey": "03E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B61", - "TransactionType": "EscrowCreate", - "TxnSignature": "30450221008AC8BDC2151D5EF956197F0E6E89A4F49DEADC1AC38367870E444B1EA8D88D97022075E31427B455DFF87F0F22B849C71FC3987A91C19D63B6D0242E808347EC8A8F", - "date": 556841101, - "hash": "E22D1F6EB006CAD35E0DBD3B4F3748427055E4C143EBE95AA6603823AEEAD324", - "inLedger": 1772019, - "ledger_index": 1772019, - "meta": { - "AffectedNodes": [ - { - "ModifiedNode": { - "LedgerEntryType": "AccountRoot", - "LedgerIndex": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8", - "PreviousTxnID": "52C4F626FE6F33699B6BE8ADF362836DDCE9B0B1294BFAA15D65D61501350BE6", - "PreviousTxnLgrSeq": 1771204 - } - }, - { - "ModifiedNode": { - "FinalFields": { - "Flags": 0, - "Owner": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB", - "RootIndex": "4B4EBB6D8563075813D47491CC325865DFD3DC2E94889F0F39D59D9C059DD81F" - }, - "LedgerEntryType": "DirectoryNode", - "LedgerIndex": "4B4EBB6D8563075813D47491CC325865DFD3DC2E94889F0F39D59D9C059DD81F" - } - }, - { - "ModifiedNode": { - "FinalFields": { - "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB", - "Balance": "9999798970", - "Flags": 0, - "OwnerCount": 1, - "Sequence": 6 - }, - "LedgerEntryType": "AccountRoot", - "LedgerIndex": "5F3B7107F4B524367A173A2B0EAB66E8CC4D2178C1B0C0528CB2F73A8B6BF254", - "PreviousFields": { - "Balance": "9999898980", - "OwnerCount": 0, - "Sequence": 5 - }, - "PreviousTxnID": "52C4F626FE6F33699B6BE8ADF362836DDCE9B0B1294BFAA15D65D61501350BE6", - "PreviousTxnLgrSeq": 1771204 - } - }, - { - "CreatedNode": { - "LedgerEntryType": "Escrow", - "LedgerIndex": "E2CF730A31FD419382350C9DBD8DB7CD775BA5AA9B97A9BE9AB07304AA217A75", - "NewFields": { - "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB", - "Amount": "100000", - "CancelAfter": 556927412, - "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120", - "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn" - } - } - } - ], - "TransactionIndex": 0, - "TransactionResult": "tesSUCCESS" - }, - "validated": true - } -} diff --git a/_api-examples/escrow/websocket/tx-response-escrowcreate-time.json b/_api-examples/escrow/websocket/tx-response-escrowcreate-time.json deleted file mode 100644 index a9bdf3cd026..00000000000 --- a/_api-examples/escrow/websocket/tx-response-escrowcreate-time.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "id": 3, - "status": "success", - "type": "response", - "result": { - "Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", - "Amount": "10000", - "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", - "Fee": "10", - "FinishAfter": 557020800, - "Flags": 2147483648, - "Sequence": 1, - "SigningPubKey": "03C3555B7339FFDDB43495A8371A3A87B4C66B67D49D06CB9BA1FDBFEEB57B6E43", - "TransactionType": "EscrowCreate", - "TxnSignature": "304402203C9AA4C21E1A1A7427D41583283E7A513DDBDD967B246CADD3B2705D858A7A8E02201BEA7B923B18910EEB9F306F6DE3B3F53549BBFAD46335B62B4C34A6DCB4A476", - "date": 557014081, - "hash": "55B2057332F8999208C43BA1E7091B423A16E5ED2736C06300B4076085205263", - "inLedger": 1828796, - "ledger_index": 1828796, - "meta": { - "AffectedNodes": [ - { - "ModifiedNode": { - "LedgerEntryType": "AccountRoot", - "LedgerIndex": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8", - "PreviousTxnID": "613B28E0890FC975F2CBA3D700F75116F623B1E3FE48CB7CB2EB216EAD6F097D", - "PreviousTxnLgrSeq": 1799920 - } - }, - { - "CreatedNode": { - "LedgerEntryType": "Escrow", - "LedgerIndex": "2B9845CB9DF686B9615BF04F3EC66095A334D985E03E71B893B90FCF6D4DC9E6", - "NewFields": { - "Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", - "Amount": "10000", - "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", - "FinishAfter": 557020800 - } - } - }, - { - "ModifiedNode": { - "FinalFields": { - "Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", - "Balance": "9999989990", - "Flags": 0, - "OwnerCount": 1, - "Sequence": 2 - }, - "LedgerEntryType": "AccountRoot", - "LedgerIndex": "AE5AB6584A76C37C7382B6880609FC7792D90CDA36FF362AF412EB914C1715D3", - "PreviousFields": { - "Balance": "10000000000", - "OwnerCount": 0, - "Sequence": 1 - }, - "PreviousTxnID": "F181D45FD094A7417926F791D9DF958B84CE4B7B3D92CC9DDCACB1D5EC59AAAA", - "PreviousTxnLgrSeq": 1828732 - } - }, - { - "CreatedNode": { - "LedgerEntryType": "DirectoryNode", - "LedgerIndex": "D623EBEEEE701D4323D0ADA5320AF35EA8CC6520EBBEF69343354CD593DABC88", - "NewFields": { - "Owner": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", - "RootIndex": "D623EBEEEE701D4323D0ADA5320AF35EA8CC6520EBBEF69343354CD593DABC88" - } - } - } - ], - "TransactionIndex": 3, - "TransactionResult": "tesSUCCESS" - }, - "validated": true - } -} diff --git a/_api-examples/escrow/websocket/tx-response-escrowfinish-condition.json b/_api-examples/escrow/websocket/tx-response-escrowfinish-condition.json deleted file mode 100644 index 683e2f2625c..00000000000 --- a/_api-examples/escrow/websocket/tx-response-escrowfinish-condition.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "id": 20, - "status": "success", - "type": "response", - "result": { - "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB", - "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120", - "Fee": "500", - "Flags": 2147483648, - "Fulfillment": "A0228020D280D1A02BAD0D2EBC0528B92E9BF37AC3E2530832C2C52620307135156F1048", - "OfferSequence": 2, - "Owner": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB", - "Sequence": 4, - "SigningPubKey": "03E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B61", - "TransactionType": "EscrowFinish", - "TxnSignature": "3045022100925FEBE21C2E57F81C472A4E5869CAB1D0164C472A46532F39F6F9F7ED6846D002202CF9D9063ADC4CC0ADF4C4692B7EE165C5D124CAA855649389E245D993F41D4D", - "date": 556838610, - "hash": "0E88368CAFC69A722ED829FAE6E2DD3575AE9C192691E60B5ACDF706E219B2BF", - "inLedger": 1771204, - "ledger_index": 1771204, - "meta": { - "AffectedNodes": [ - { - "ModifiedNode": { - "FinalFields": { - "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", - "Balance": "400100000", - "Flags": 0, - "OwnerCount": 0, - "Sequence": 1 - }, - "LedgerEntryType": "AccountRoot", - "LedgerIndex": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8", - "PreviousFields": { - "Balance": "400000000" - }, - "PreviousTxnID": "795CBC8AFAAB9DC7BD9944C7FAEABF9BB0802A84520BC649213AD6A2C3256C95", - "PreviousTxnLgrSeq": 1770775 - } - }, - { - "ModifiedNode": { - "FinalFields": { - "Flags": 0, - "Owner": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB", - "RootIndex": "4B4EBB6D8563075813D47491CC325865DFD3DC2E94889F0F39D59D9C059DD81F" - }, - "LedgerEntryType": "DirectoryNode", - "LedgerIndex": "4B4EBB6D8563075813D47491CC325865DFD3DC2E94889F0F39D59D9C059DD81F" - } - }, - { - "ModifiedNode": { - "FinalFields": { - "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB", - "Balance": "9999898980", - "Flags": 0, - "OwnerCount": 0, - "Sequence": 5 - }, - "LedgerEntryType": "AccountRoot", - "LedgerIndex": "5F3B7107F4B524367A173A2B0EAB66E8CC4D2178C1B0C0528CB2F73A8B6BF254", - "PreviousFields": { - "Balance": "9999899480", - "OwnerCount": 1, - "Sequence": 4 - }, - "PreviousTxnID": "5C2A1E7B209A7404D3722A010D331A8C1C853109A47DDF620DE5E3D59F026581", - "PreviousTxnLgrSeq": 1771042 - } - }, - { - "DeletedNode": { - "FinalFields": { - "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB", - "Amount": "100000", - "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120", - "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", - "FinishAfter": 556838185, - "Flags": 0, - "OwnerNode": "0000000000000000", - "PreviousTxnID": "795CBC8AFAAB9DC7BD9944C7FAEABF9BB0802A84520BC649213AD6A2C3256C95", - "PreviousTxnLgrSeq": 1770775 - }, - "LedgerEntryType": "Escrow", - "LedgerIndex": "DC524D17B3F650E7A215B332F418E54AE59B0DFC5392E74958B0037AFDFE8C8D" - } - } - ], - "TransactionIndex": 1, - "TransactionResult": "tesSUCCESS" - }, - "validated": true - } -} diff --git a/_api-examples/escrow/websocket/tx-response-escrowfinish-time.json b/_api-examples/escrow/websocket/tx-response-escrowfinish-time.json deleted file mode 100644 index 3c60831d270..00000000000 --- a/_api-examples/escrow/websocket/tx-response-escrowfinish-time.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "id": 21, - "status": "success", - "type": "response", - "result": { - "Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", - "Fee": "10", - "Flags": 2147483648, - "OfferSequence": 1, - "Owner": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", - "Sequence": 2, - "SigningPubKey": "03C3555B7339FFDDB43495A8371A3A87B4C66B67D49D06CB9BA1FDBFEEB57B6E43", - "TransactionType": "EscrowFinish", - "TxnSignature": "3045022100923B91BA4FD6450813F5335D71C64BA9EB81304A86859A631F2AD8571424A46502200CCE660D36781B84634C5F23619EB6CFCCF942709F54DCCF27CF6F499AE78C9B", - "date": 557256681, - "hash": "41856A742B3CAF307E7B4D0B850F302101F0F415B785454F7501E9960A2A1F6B", - "inLedger": 1908257, - "ledger_index": 1908257, - "meta": { - "AffectedNodes": [ - { - "ModifiedNode": { - "FinalFields": { - "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", - "Balance": "400210000", - "Flags": 0, - "OwnerCount": 0, - "Sequence": 1 - }, - "LedgerEntryType": "AccountRoot", - "LedgerIndex": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8", - "PreviousFields": { - "Balance": "400200000" - }, - "PreviousTxnID": "55B2057332F8999208C43BA1E7091B423A16E5ED2736C06300B4076085205263", - "PreviousTxnLgrSeq": 1828796 - } - }, - { - "DeletedNode": { - "FinalFields": { - "Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", - "Amount": "10000", - "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", - "FinishAfter": 557020800, - "Flags": 0, - "OwnerNode": "0000000000000000", - "PreviousTxnID": "55B2057332F8999208C43BA1E7091B423A16E5ED2736C06300B4076085205263", - "PreviousTxnLgrSeq": 1828796 - }, - "LedgerEntryType": "Escrow", - "LedgerIndex": "2B9845CB9DF686B9615BF04F3EC66095A334D985E03E71B893B90FCF6D4DC9E6" - } - }, - { - "ModifiedNode": { - "FinalFields": { - "Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", - "Balance": "9999989980", - "Flags": 0, - "OwnerCount": 0, - "Sequence": 3 - }, - "LedgerEntryType": "AccountRoot", - "LedgerIndex": "AE5AB6584A76C37C7382B6880609FC7792D90CDA36FF362AF412EB914C1715D3", - "PreviousFields": { - "Balance": "9999989990", - "OwnerCount": 1, - "Sequence": 2 - }, - "PreviousTxnID": "55B2057332F8999208C43BA1E7091B423A16E5ED2736C06300B4076085205263", - "PreviousTxnLgrSeq": 1828796 - } - }, - { - "ModifiedNode": { - "FinalFields": { - "Flags": 0, - "Owner": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", - "RootIndex": "D623EBEEEE701D4323D0ADA5320AF35EA8CC6520EBBEF69343354CD593DABC88" - }, - "LedgerEntryType": "DirectoryNode", - "LedgerIndex": "D623EBEEEE701D4323D0ADA5320AF35EA8CC6520EBBEF69343354CD593DABC88" - } - } - ], - "TransactionIndex": 2, - "TransactionResult": "tesSUCCESS" - }, - "validated": true - } -} diff --git a/_code-samples/escrow/py/generate_condition.py b/_code-samples/escrow/py/generate_condition.py deleted file mode 100644 index 58b9de70cd8..00000000000 --- a/_code-samples/escrow/py/generate_condition.py +++ /dev/null @@ -1,19 +0,0 @@ -import random -from os import urandom - -from cryptoconditions import PreimageSha256 - -# """Generate a condition and fulfillment for escrows""" - -# Generate a random preimage with at least 32 bytes of cryptographically-secure randomness. -secret = urandom(32) - -# Generate cryptic image from secret -fufill = PreimageSha256(preimage=secret) - -# Parse image and return the condition and fulfillment -condition = str.upper(fufill.condition_binary.hex()) # conditon -fulfillment = str.upper(fufill.serialize_binary().hex()) # fulfillment - -# Print condition and fulfillment -print(f"condition: {condition} \n fulfillment {fulfillment}") diff --git a/_code-samples/escrow/py/send_conditional_escrow.py b/_code-samples/escrow/py/send_conditional_escrow.py index 8e33387c690..9863d47394d 100644 --- a/_code-samples/escrow/py/send_conditional_escrow.py +++ b/_code-samples/escrow/py/send_conditional_escrow.py @@ -1,5 +1,5 @@ import json -from datetime import datetime, timedelta +from datetime import datetime, timedelta, UTC from os import urandom from cryptoconditions import PreimageSha256 @@ -28,7 +28,7 @@ # Set the escrow expiration ------------------------------------------------- cancel_delay = 300 -cancel_after = datetime.now() + timedelta(seconds=cancel_delay) +cancel_after = datetime.now(tz=UTC) + timedelta(seconds=cancel_delay) print("This escrow will expire after", cancel_after) cancel_after_rippletime = datetime_to_ripple_time(cancel_after) diff --git a/_code-samples/escrow/py/send_timed_escrow.py b/_code-samples/escrow/py/send_timed_escrow.py index 2d164f2f580..c0f53a68abb 100644 --- a/_code-samples/escrow/py/send_timed_escrow.py +++ b/_code-samples/escrow/py/send_timed_escrow.py @@ -1,7 +1,6 @@ import json -from datetime import datetime, timedelta +from datetime import datetime, timedelta, UTC from time import sleep -from os import urandom from xrpl.clients import JsonRpcClient from xrpl.models import EscrowCreate, EscrowFinish @@ -21,8 +20,8 @@ # Set the escrow finish time ------------------------------------------------ delay = 30 -finish_after = datetime.now() + timedelta(seconds=delay) -print("This escrow will expire after", finish_after) +finish_after = datetime.now(tz=UTC) + timedelta(seconds=delay) +print("This escrow will mature after", finish_after) finish_after_rippletime = datetime_to_ripple_time(finish_after) # Send EscrowCreate transaction --------------------------------------------- @@ -45,6 +44,7 @@ # Save the sequence number so you can identify the escrow later escrow_seq = response.result["tx_json"]["Sequence"] +print(f"Escrow sequence is {escrow_seq}.") # Wait for the escrow to be finishable -------------------------------------- sleep(delay) @@ -68,7 +68,7 @@ sleep(time_difference) -# Send the EscrowFinish transaction ----------------------------------------- +# Send EscrowFinish transaction --------------------------------------------- escrow_finish = EscrowFinish( account=wallet.address, owner=wallet.address, diff --git a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow.md b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow.md new file mode 100644 index 00000000000..d43b3fbed63 --- /dev/null +++ b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow.md @@ -0,0 +1,140 @@ +--- +seo: + description: Create an escrow whose release is based on a condition being fulfilled. +labels: + - Escrow +--- +# Send a Conditional Escrow + +This tutorial demonstrates how to send an [escrow](../../../../concepts/payment-types/escrow.md) that can be released when a specific crypto-condition is fulfilled. Essentially, a crypto-condition is like a random password that unlocks the escrow to be sent to its indicated destination. You can use this as part of an app that reveals the fulfillment only when specific actions take place. + +This tutorial shows how to escrow XRP. If the [TokenEscrow amendment][] is enabled, you can also escrow tokens. + +## Goals + +By following this tutorial, you should learn how to: + +- Convert a timestamp into the XRP Ledger's native format. +- Create and finish an escrow. + +## Prerequisites + +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. + +## Source Code + +You can find the complete source code for this tutorial's examples in the {% repo-link path="_code-samples/escrow/" %}code samples section of this website's repository{% /repo-link %}. + +## Steps + +### 1. Install dependencies + +{% tabs %} +{% tab label="JavaScript" %} +From the code sample folder, use `npm` to install dependencies: + +```sh +npm i +``` +{% /tab %} + +{% tab label="Python" %} +From the code sample folder, set up a virtual environment and use `pip` to install dependencies: + +```sh +python -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +``` +{% /tab %} +{% /tabs %} + +### 2. Set up client and account + +To get started, import the client library and instantiate an API client. For this tutorial, you also need one account, which you can get from the faucet. You also need the address of another account to send the escrow to. You can fund a second account using the faucet, or use the address of an existing account like the faucet. + +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/escrow/js/send-conditional-escrow.js" language="js" before="// Create the crypto-condition" /%} +{% /tab %} +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/send_conditional_escrow.py" language="py" before="# Create the crypto-condition" /%} +{% /tab %} +{% /tabs %} + +### 3. Create a condition and fulfillment + +Conditional escrows require a fulfillment and its corresponding condition in the format of a PREIMAGE-SHA-256 _crypto-condition_, represented as hexadecimal. To calculate these in the correct format, use a crypto-conditions library. Generally, you want to generate the fulfillment using at least 32 random bytes from a cryptographically secure source of randomness. + +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/escrow/js/send-conditional-escrow.js" language="js" from="// Create the crypto-condition" before="// Set the escrow expiration" /%} +{% /tab %} +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/send_conditional_escrow.py" language="py" from="# Create the crypto-condition" before="# Set the escrow expiration" /%} +{% /tab %} +{% /tabs %} + +### 4. Calculate the expiration time + +Conditional escrows also need an expiration time, so that the escrow can be canceled if the correct fulfillment isn't provided by the scheduled time. This timestamp must be formatted as [seconds since the Ripple Epoch][]. The sample code calculates an expiration time 30 seconds after the current time. + +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/escrow/js/send-conditional-escrow.js" language="js" from="// Set the escrow expiration" before="// Send EscrowCreate transaction" /%} +{% /tab %} +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/send_conditional_escrow.py" language="py" from="# Set the escrow expiration" before="# Send EscrowCreate transaction" /%} +{% /tab %} +{% /tabs %} + +### 5. Create the escrow + +To send the escrow, construct an [EscrowCreate transaction][] and then submit it to the network. The fields of this transaction define the properties of the escrow. The sample code uses hard-coded values to send 0.123456 XRP back to the Testnet faucet: + +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/escrow/js/send-conditional-escrow.js" language="js" from="// Send EscrowCreate transaction" before="// Save the sequence number" /%} +{% /tab %} + +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/send_conditional_escrow.py" language="py" from="# Send EscrowCreate transaction" before="# Save the sequence number" /%} +{% /tab %} +{% /tabs %} + +### 6. Finish the escrow + +Anyone with the correct fulfillment can immediately finish a conditional escrow (unless it's a timed conditinal escrow with a `FinishAfter` time). To do this, construct an [EscrowFinish transaction][], using the sequence number that you recorded when you created the escrow, and the matching condition and fulfillment for the escrow, then submit it to the network. + +{% admonition type="warning" name="Caution" %}A conditional EscrowFinish requires a [higher than normal transaction cost](../../../../concepts/transactions/transaction-cost.md#special-transaction-costs) based on the size of the fulfillment in bytes. Most libraries should specify an appropriate amount of XRP when autofilling, but you should be mindful of this when specifying the `Fee` field manually.{% /admonition %} + +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/escrow/js/send-conditional-escrow.js" language="js" from="// Send EscrowFinish transaction" /%} +{% /tab %} + +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/send_conditional_escrow.py" language="py" from="# Send EscrowFinish transaction" /%} +{% /tab %} +{% /tabs %} + + +## See Also + +- [Crypto-Conditions Specification][] +- **Concepts:** + - [Escrow](../../../../concepts/payment-types/escrow.md) +- **Tutorials:** + - [Send XRP](../../send-xrp.md) + - [Look Up Transaction Results](../../../../concepts/transactions/finality-of-results/look-up-transaction-results.md) + - [Reliable Transaction Submission](../../../../concepts/transactions/reliable-transaction-submission.md) +- **References:** + - [EscrowCancel transaction][] + - [EscrowCreate transaction][] + - [EscrowFinish transaction][] + - [Escrow ledger object](../../../../references/protocol/ledger-data/ledger-entry-types/escrow.md) + +{% raw-partial file="/docs/_snippets/common-links.md" /%} diff --git a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md deleted file mode 100644 index 61cf19c16f0..00000000000 --- a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md +++ /dev/null @@ -1,214 +0,0 @@ ---- -html: send-a-conditionally-held-escrow.html -parent: use-escrows.html -seo: - description: Create an escrow whose release is based on a condition being fulfilled. -labels: - - Escrow - - Smart Contracts ---- -# Send a Conditionally-Held Escrow - -## 1. Generate condition and fulfillment - -XRP Ledger escrows require PREIMAGE-SHA-256 [crypto-conditions][]. To calculate a condition and fulfillment in the proper format, you should use a crypto-conditions library such as [five-bells-condition](https://github.com/interledgerjs/five-bells-condition). To generate the fulfillment: - -- Use a cryptographically secure source of randomness to generate at least 32 random bytes. -- Follow Interledger Protocol's [PSK specification](https://github.com/interledger/rfcs/blob/master/deprecated/0016-pre-shared-key/0016-pre-shared-key.md) and use an HMAC-SHA-256 of the ILP packet as the fulfillment. - -Example code for a random fulfillment and condition: - -{% tabs %} - -{% tab label="JavaScript" %} -```js -const cc = require('five-bells-condition') -const crypto = require('crypto') - -const preimageData = crypto.randomBytes(32) -const fulfillment = new cc.PreimageSha256() -fulfillment.setPreimage(preimageData) - -const condition = fulfillment.getConditionBinary().toString('hex').toUpperCase() -console.log('Condition:', condition) - -// Keep secret until you want to finish the escrow -const fulfillment_hex = fulfillment.serializeBinary().toString('hex').toUpperCase() -console.log('Fulfillment:', fulfillment_hex) -``` -{% /tab %} - -{% tab label="Python" %} -```py -from os import urandom -from cryptoconditions import PreimageSha256 - -secret = urandom(32) - -fulfillment = PreimageSha256(preimage=secret) - -print("Condition", fulfillment.condition_binary.hex().upper()) - -# Keep secret until you want to finish the escrow -print("Fulfillment", fulfillment.serialize_binary().hex().upper()) -``` -{% /tab %} - -{% /tabs %} - -Save the condition and the fulfillment for later. Be sure to keep the fulfillment secret until you want to finish executing the held payment. Anyone who knows the fulfillment can finish the escrow, releasing the held funds to their intended destination. - - -## 2. Calculate release or cancel time - -A Conditional `Escrow` transaction must contain either a `CancelAfter` or `FinishAfter` field, or both. The `CancelAfter` field lets the XRP revert to the sender if the condition is not fulfilled before the specified time. The `FinishAfter` field specifies a time before which the escrow cannot execute, even if someone sends the correct fulfillment. Whichever field you provide, the time it specifies must be in the future. - -Example for setting a `CancelAfter` time of 24 hours in the future: - -{% tabs %} - -{% tab label="JavaScript" %} -```js -const rippleOffset = 946684800 -const CancelAfter = Math.floor(Date.now() / 1000) + (24*60*60) - rippleOffset -console.log(CancelAfter) -// Example: 556927412 -``` -{% /tab %} - -{% tab label="Python 2/3" %} -```python -from time import time -ripple_offset = 946684800 -cancel_after = int(time()) + (24*60*60) - 946684800 -print(cancel_after) -# Example: 556927412 -``` -{% /tab %} - -{% /tabs %} - -{% admonition type="danger" name="Warning" %}In the XRP Ledger, you must specify time as **[seconds since the Ripple Epoch][]**. If you use a UNIX time in the `CancelAfter` or `FinishAfter` field without converting it, that sets the unlock time to an extra **30 years** in the future!{% /admonition %} - -## 3. Submit EscrowCreate transaction - -[Sign and submit](../../../../concepts/transactions/index.md#signing-and-submitting-transactions) an [EscrowCreate transaction][]. Set the `Condition` field of the transaction to the time when the held payment should be released. Set the `Destination` to the recipient, which can be the same address as the sender. Include the `CancelAfter` or `FinishAfter` time you calculated in the previous step. Set the `Amount` to the total amount of [XRP, in drops][], to escrow. - -{% partial file="/docs/_snippets/secret-key-warning.md" /%} - -{% tabs %} - -{% tab label="Websocket" %} -Request: -{% code-snippet file="/_api-examples/escrow/websocket/submit-request-escrowcreate-condition.json" language="json" /%} - -Response: -{% code-snippet file="/_api-examples/escrow/websocket/submit-response-escrowcreate-condition.json" language="json" /%} -{% /tab %} - -{% tab label="Python" %} -{% code-snippet file="/_code-samples/escrow/py/create_escrow.py" language="py" from="# Build escrow create" /%} -{% /tab %} - -{% /tabs %} - - - -## 4. Wait for validation - -{% raw-partial file="/docs/_snippets/wait-for-validation.md" /%} - -## 5. Confirm that the escrow was created - -Use the [tx method][] with the transaction's identifying hash to check its final status. In particular, look for a `CreatedNode` in the transaction metadata to indicate that it created an [Escrow ledger object](../../../../concepts/payment-types/escrow.md). - -Request: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/tx-request-escrowcreate-condition.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -Response: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/tx-response-escrowcreate-condition.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -## 6. Submit EscrowFinish transaction - -[Sign and submit](../../../../concepts/transactions/index.md#signing-and-submitting-transactions) an [EscrowFinish transaction][] to execute the release of the funds after the `FinishAfter` time has passed. Set the `Owner` field of the transaction to the `Account` address from the EscrowCreate transaction, and the `OfferSequence` to the `Sequence` number from the EscrowCreate transaction. Set the `Condition` and `Fulfillment` fields to the condition and fulfillment values, in hexadecimal, that you generated in step 1. Set the `Fee` ([transaction cost](../../../../concepts/transactions/transaction-cost.md)) value based on the size of the fulfillment in bytes: a conditional EscrowFinish requires at least 330 drops of XRP plus 10 drops per 16 bytes in the size of the fulfillment. - -{% admonition type="info" name="Note" %}If you included a `FinishAfter` field in the EscrowCreate transaction, you cannot execute it before that time has passed, even if you provide the correct fulfillment for the Escrow's condition. The EscrowFinish transaction fails with the [result code](../../../../references/protocol/transactions/transaction-results/index.md) `tecNO_PERMISSION` if the previously-closed ledger's close time is before the `FinishAfter` time.{% /admonition %} - -If the escrow has expired, you can only [cancel the escrow](cancel-an-expired-escrow.md) instead. - -{% partial file="/docs/_snippets/secret-key-warning.md" /%} - -{% tabs %} - -{% tab label="Websocket" %} -Request: -{% code-snippet file="/_api-examples/escrow/websocket/submit-request-escrowfinish-condition.json" language="json" /%} - -Response: -{% code-snippet file="/_api-examples/escrow/websocket/submit-response-escrowfinish-condition.json" language="json" /%} -{% /tab %} - -{% tab label="Javascript" %} -{% code-snippet file="/_code-samples/escrow/js/finish-escrow.js" language="js" from="// Prepare EscrowFinish" before="await client.disconnect" /%} -{% /tab %} - -{% tab label="Python" %} -{% code-snippet file="/_code-samples/escrow/py/finish_escrow.py" language="py" from="# Build escrow finish" /%} -{% /tab %} - -{% /tabs %} - -Take note of the transaction's identifying `hash` value so you can check its final status when it is included in a validated ledger version. - -## 7. Wait for validation - -{% raw-partial file="/docs/_snippets/wait-for-validation.md" /%} - -## 8. Confirm final result - -Use the [tx method][] with the EscrowFinish transaction's identifying hash to check its final status. In particular, look in the transaction metadata for a `ModifiedNode` of type `AccountRoot` for the destination of the escrowed payment. The `FinalFields` of the object should show the increase in XRP in the `Balance` field. - -Request: - -{% code-snippet file="/_api-examples/escrow/websocket/tx-request-escrowfinish-condition.json" language="json" /%} - -Response: - -{% code-snippet file="/_api-examples/escrow/websocket/tx-response-escrowfinish-condition.json" language="json" /%} - - - -## See Also - -- [Crypto-Conditions Specification][] -- **Concepts:** - - [What is XRP?](../../../../introduction/what-is-xrp.md) - - [Payment Types](../../../../concepts/payment-types/index.md) - - [Escrow](../../../../concepts/payment-types/escrow.md) -- **Tutorials:** - - [Send XRP](../../send-xrp.md) - - [Look Up Transaction Results](../../../../concepts/transactions/finality-of-results/look-up-transaction-results.md) - - [Reliable Transaction Submission](../../../../concepts/transactions/reliable-transaction-submission.md) -- **References:** - - [EscrowCancel transaction][] - - [EscrowCreate transaction][] - - [EscrowFinish transaction][] - - [account_objects method][] - - [tx method][] - - [Escrow ledger object](../../../../references/protocol/ledger-data/ledger-entry-types/escrow.md) - -{% raw-partial file="/docs/_snippets/common-links.md" /%} diff --git a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md index 9f476153dae..30ff53c17ac 100644 --- a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md +++ b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md @@ -26,7 +26,7 @@ To complete this tutorial, you should: ## Source Code -You can find the complete source code for this tutorial's examples in the {% repo-link path="_code-samples/escrow/send-timed-escrow.js" %}code samples section of this website's repository{% /repo-link %}. +You can find the complete source code for this tutorial's examples in the {% repo-link path="_code-samples/escrow/" %}code samples section of this website's repository{% /repo-link %}. ## Steps @@ -34,22 +34,35 @@ You can find the complete source code for this tutorial's examples in the {% rep {% tabs %} {% tab label="JavaScript" %} -From the code sample folder, use npm to install dependencies: +From the code sample folder, use `npm` to install dependencies: ```sh npm i ``` {% /tab %} + +{% tab label="Python" %} +From the code sample folder, set up a virtual environment and use `pip` to install dependencies: + +```sh +python -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +``` +{% /tab %} {% /tabs %} ### 2. Set up client and account -To get started, import the client library and instantiate an API client. For this tutorial, you also need one account, which you can get from the faucet. +To get started, import the client library and instantiate an API client. For this tutorial, you also need one account, which you can get from the faucet. You also need the address of another account to send the escrow to. You can fund a second account using the faucet, or use the address of an existing account like the faucet. {% tabs %} {% tab label="JavaScript" %} {% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" before="// Set the escrow finish time" /%} {% /tab %} +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/send_timed_escrow.py" language="py" before="# Set the escrow finish time" /%} +{% /tab %} {% /tabs %} ### 3. Calculate the finish time @@ -60,6 +73,10 @@ To make a timed escrow, you need to set the maturity time of the escrow, which i {% tab label="JavaScript" %} {% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="// Set the escrow finish time" before="// Send EscrowCreate transaction" /%} {% /tab %} + +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/send_timed_escrow.py" language="py" from="# Set the escrow finish time" before="# Send EscrowCreate transaction" /%} +{% /tab %} {% /tabs %} {% admonition type="danger" name="Warning" %}If you use a UNIX time without converting to the equivalent Ripple time first, that sets the maturity time to an extra **30 years** in the future!{% /admonition %} @@ -73,13 +90,25 @@ To send the escrow, construct an [EscrowCreate transaction][] and then submit it {% tabs %} {% tab label="JavaScript" %} {% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="// Send EscrowCreate transaction" before="// Save the sequence number" /%} +{% /tab %} + +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/send_timed_escrow.py" language="py" from="# Send EscrowCreate transaction" before="# Save the sequence number" /%} +{% /tab %} +{% /tabs %} {% admonition type="info" name="Note" %}To give the escrow an expiration time, add a `CancelAfter` field to the transaction. An expiration time is optional for timed XRP escrows but required for token escrows. This time must be after the maturity time.{% /admonition %} Save the sequence number of the EscrowCreate transaction. (In this example, the sequence number is autofilled.) You need this sequence number to identify the escrow when you want to finish (or cancel) it later. +{% tabs %} +{% tab label="JavaScript" %} {% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="// Save the sequence number" before="// Wait for the escrow" /%} {% /tab %} + +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/send_timed_escrow.py" language="py" from="# Save the sequence number" before="# Wait for the escrow" /%} +{% /tab %} {% /tabs %} @@ -95,6 +124,10 @@ JavaScript doesn't have a native `sleep(...)` function, but you can implement on {% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="/* Sleep function" before="// Check if escrow can be finished" /%} {% /tab %} + +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/send_timed_escrow.py" language="py" from="# Wait for the escrow" before="# Check if escrow can be finished" /%} +{% /tab %} {% /tabs %} At this point, the escrow should be mature, but that depends on the official close time of the previous ledger. Ledger close times can vary based on the consensus process, and [are rounded](../../../../concepts/ledgers/ledger-close-times.md) by up to 10 seconds. To account for this variance, use an approach such as the following: @@ -107,6 +140,10 @@ At this point, the escrow should be mature, but that depends on the official clo {% tab label="JavaScript" %} {% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="// Check if escrow can be finished" before="// Send EscrowFinish transaction" /%} {% /tab %} + +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/send_timed_escrow.py" language="py" from="# Check if escrow can be finished" before="# Send EscrowFinish transaction" /%} +{% /tab %} {% /tabs %} ### 6. Finish the escrow @@ -119,15 +156,17 @@ Now that the escrow is mature, you can finish it. Construct an [EscrowFinish tra {% tab label="JavaScript" %} {% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="// Send EscrowFinish transaction" /%} {% /tab %} + +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/send_timed_escrow.py" language="py" from="# Send EscrowFinish transaction" /%} +{% /tab %} {% /tabs %} ## See Also - **Concepts:** - - [What is XRP?](../../../../introduction/what-is-xrp.md) - - [Payment Types](../../../../concepts/payment-types/index.md) - - [Escrow](../../../../concepts/payment-types/escrow.md) + - [Escrow](../../../../concepts/payment-types/escrow.md) - **Tutorials:** - [Send XRP](../../send-xrp.md) - [Look Up Transaction Results](../../../../concepts/transactions/finality-of-results/look-up-transaction-results.md) @@ -136,8 +175,6 @@ Now that the escrow is mature, you can finish it. Construct an [EscrowFinish tra - [EscrowCancel transaction][] - [EscrowCreate transaction][] - [EscrowFinish transaction][] - - [account_objects method][] - - [tx method][] - [Escrow ledger object](../../../../references/protocol/ledger-data/ledger-entry-types/escrow.md) {% raw-partial file="/docs/_snippets/common-links.md" /%} diff --git a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/use-an-escrow-as-a-smart-contract.md b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/use-an-escrow-as-a-smart-contract.md deleted file mode 100644 index f9a67e49114..00000000000 --- a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/use-an-escrow-as-a-smart-contract.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -html: use-an-escrow-as-a-smart-contract.html -parent: use-escrows.html -seo: - description: Use a cryptographic escrow as a smart contract to ensure a recipient gets paid only if they successfully perform a service. -labels: - - Escrow - - Smart Contracts ---- -# Use an Escrow as a Smart Contract - -A smart contract is a blockchain-based program that encodes the conditions and fulfillment of an agreement between two or more parties and automatically fulfills the terms of the agreement once conditions are met. A smart contract can help you exchange anything of value in a transparent, traceable, tamper-resistant, and irreversible way. - -The benefit of encoding a smart contract into a blockchain is that it enables the contract to be securely carried out without traditional third-parties, like financial or legal institutions. Instead, the contract is supervised by the distributed, decentralized network of computers that run the blockchain. - -You can use XRP Ledger escrows as smart contracts that release XRP after a certain time has passed or after a cryptographic condition has been fulfilled. In this case, we'll use an escrow as a smart contract that releases XRP after a cryptographic condition has been fulfilled. - -Let's use this scenario to help illustrate this use case: A party planner uses smart contracts to manage payments from party hosts to party vendors. Specifically, the party planner wants to use a smart contract to have the party host pay the party band 2000 XRP once they are done with their set. - -In this use case, the party host is the sender of the escrow, the party band is the receiver of the escrow, and the party planner is playing the role of an _oracle_. In the context of smart contracts, an oracle is a neutral third-party agent that can verify real-world events to either fulfill or invalidate a smart contract. This use case uses a human oracle for illustrative purposes, but in real-life, a software application would more likely play the role of the oracle. - -Using an XRP Ledger escrow to provide this smart contract is a great arrangement because the party planner, as the third-party oracle, never "holds" the funds as one might in a traditional escrow arrangement, and can't possibly take the funds for themselves. - -Here’s a roadmap to the high-level tasks that these participants need to complete to use an escrow as a smart contract. - - -## Meet the prerequisites - -The party host (sender) must have: - -- An XRP Ledger [account](../../../../concepts/accounts/index.md#creating-accounts) that holds enough XRP to pay for escrow and any fees incurred. - -- Access to a secure signing environment, which includes having a network connection to a [`rippled` server](../../../../infrastructure/installation/index.md) (any server) that they can submit signed transactions to. - -The party band (receiver) must have: - -- An XRP Ledger [account](../../../../concepts/accounts/index.md#creating-accounts) that can receive the XRP paid by the escrow. - -- Access to a [`rippled` server](../../../../infrastructure/installation/index.md) that they can use to look up the details of an XRP Ledger transaction hash and submit the fulfillment value to finish the escrow. - -The party planner (oracle) must have: - -- The ability to generate a condition and a fulfillment. - -- To be able to keep a secret (the fulfillment) until the time is right. - -- A way to communicate the fulfillment publicly or at least to the party band when the time is right. - -- The ability to recognize whether the party band has fulfilled their end of the contract (played at the party). - - - - -## Define the terms of the smart contract - -To create the escrow as a smart contract, the participants must first define the terms of the contract. In this scenario, the participants need to agree on the following details. - -- **Should the escrow disallow fulfillment until a specific time?** - - ``` - While this is an option, the participants agree that it is unnecessary for their escrow. For conditionally-held escrows, enabling this option doesn't provide any additional security, since whether the escrow can be finished still depends entirely on whether the party planner (oracle) publishes the fulfillment before the expiration. - ``` - -- **Should the escrow expire?** - - ``` - Absolutely yes. The participants agree that the escrow should expire after 12 noon the day after the party. This gives the party band (receiver) enough time to finish the escrow, after the party planner verifies that they fulfilled their end of the contract and publishes the cryptographic fulfillment. After expiration, the locked XRP returns to the party host's (sender's) account. - - If the participants don't allow the escrow to expire and the party planner doesn't release the condition, the XRP stays locked in the escrow forever. - ``` - -- **How much XRP should the escrow lock up and potentially pay?** - - ``` - The participants agree that the escrow should lock up and potentially pay 2000 XRP, which is the party band's fee. - ``` - -- **From which XRP Ledger account should the escrow lock up XRP for potential payment to the party band?** - - ``` - The participants agree that the escrow should lock up and potentially pay XRP out of the party host's XRP Ledger account. - ``` - -- **Which XRP Ledger account should the escrow potentially pay XRP to?** - - ``` - The participants agree that the escrow should potentially pay XRP to the party band's XRP Ledger account. - ``` - - - - -## Oracle: Generate a condition and a fulfillment - -Because participants want to create a conditionally-held escrow to provide the smart contract, they need a condition value and a fulfillment value. In this scenario, the participant that creates these values is the neutral party planner (oracle). - -The party planner generates the condition and fulfillment values. The party planner provides the condition value to the party host, who creates the escrow. The party planner also provides the condition to the party band so that they know that this is the right condition. - -The party planner must keep the fulfillment value a secret. Anyone can use the condition and fulfillment values to finish the escrow. Most often, the receiver finishes the escrow because they're the ones who are motivated to get paid. - -[Generate a condition and a fulfillment >](send-a-conditionally-held-escrow.md#1-generate-condition-and-fulfillment) - - -## Sender: Calculate time values needed for the escrow - -Because the participants want the escrow to be eligible for cancellation after 12 noon the day after the party, the party host (sender) must calculate a `CancelAfter` value to include in the escrow definition. - -[Calculate time values needed for the escrow >](send-a-conditionally-held-escrow.md#2-calculate-release-or-cancel-time) - - - -## Sender: Create the escrow - -The party host (sender) creates the escrow that provides the smart contract. The party host must create the escrow because they are the only participant that can authorize the lock up and potential payout of XRP from their XRP Ledger account. - -[Create the escrow >](send-a-conditionally-held-escrow.md#3-submit-escrowcreate-transaction) - - - -## Sender and Receiver: Wait for validation and confirm escrow creation - -The party host (sender) waits for validation of the ledger that contains the escrow creation transaction and then confirms that the escrow was created. - -[Wait for validation >](send-a-conditionally-held-escrow.md#4-wait-for-validation) - -The party host then provides the escrow transaction's `hash` value to the party band (receiver). The party band can use the `hash` value to look up the escrow transaction on the XRP Ledger to ensure that it was created according to the smart contract terms they agreed to. As part of this step, the party band should confirm that the condition matches the one the party planner (oracle) provided. If the condition is wrong, the fulfillment the party planner provides won't let the party band finish the escrow and get paid. - -[confirm escrow creation >](send-a-conditionally-held-escrow.md#5-confirm-that-the-escrow-was-created) - - - -## Receiver: Finish the escrow - -The party band (receiver) shows up and plays their set. - -The party planner (oracle) is present at the party to ensure that everything is going smoothly. The party planner confirms first-hand that the party band has fulfilled their contract and publishes the fulfillment publicly, or at least to the party band. - -The party band must finish the escrow before 12 noon. If they don't, the escrow expires and the party band doesn't get paid. - -If the party planner does not publish the fulfillment (the party band is a no show) or if the party planner publishes the fulfillment, but no one finishes the escrow; after 12 noon the next day, anyone can [cancel the escrow](cancel-an-expired-escrow.md). Cancelling the escrow returns the held XRP to the party host's account. - -[Finish the escrow >](send-a-conditionally-held-escrow.md#6-submit-escrowfinish-transaction) - - - -## Receiver and Sender: Wait for validation and confirm final result - -The party band (receiver) waits for validation of the ledger that contains the escrow finish transaction and then confirms that the escrow was finished. - -At this time, the party band provides the transaction's `hash` value to the party host (sender). They can use the `hash` value to look up the escrow transaction on the XRP Ledger to ensure that it is been finished correctly. - -The party band can check their XRP Ledger account balance to ensure that their balance has increased by 2000 XRP. The party host's balance won't change at this step (unless the escrow was canceled) because the escrow creation already debited the locked-up XRP from their account. - -[Wait for validation >](send-a-conditionally-held-escrow.md#7-wait-for-validation) - -[confirm final result >](send-a-conditionally-held-escrow.md#8-confirm-final-result) diff --git a/docs/use-cases/payments/smart-contracts-uc.md b/docs/use-cases/payments/smart-contracts-uc.md index 08b52b82b8a..60f34949e01 100644 --- a/docs/use-cases/payments/smart-contracts-uc.md +++ b/docs/use-cases/payments/smart-contracts-uc.md @@ -1,6 +1,4 @@ --- -html: escrow-uc.html -parent: payments-uc.html seo: description: Transactions allow accounts to modify the XRP Ledger. labels: @@ -45,7 +43,7 @@ The oracle gives the condition hex value to the escrow creator, enabling them to After the oracle's programming detects the conditions are met, it gives the fulfillment hex value to the escrow recipient. It does nothing else after this point, such as finishing the escrow. The recipient of the escrow would most likely finish the escrow. -See: [Generate a condition and fulfillment](../../tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md#1-generate-condition-and-fulfillment). +See: [Send a conditional escrow](../../tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow.md). ## Examples diff --git a/redirects.yaml b/redirects.yaml index 018d10ead08..5544da1d4e3 100644 --- a/redirects.yaml +++ b/redirects.yaml @@ -734,7 +734,7 @@ send-a-time-held-escrow.html: to: /docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-time-held-escrow type: 301 send-a-conditionally-held-escrow.html: - to: /docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow + to: /docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow type: 301 cancel-an-expired-escrow.html: to: /docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/cancel-an-expired-escrow @@ -743,7 +743,7 @@ look-up-escrows.html: to: /docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/look-up-escrows type: 301 use-an-escrow-as-a-smart-contract.html: - to: /docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/use-an-escrow-as-a-smart-contract + to: /docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow type: 301 use-payment-channels.html: to: /docs/tutorials/how-tos/use-specialized-payment-types/use-payment-channels @@ -3062,7 +3062,7 @@ code_of_conduct.ja: to: /ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-time-held-escrow type: 301 /ja/send-a-conditionally-held-escrow.html: - to: /ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow + to: /ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow type: 301 /ja/cancel-an-expired-escrow.html: to: /ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/cancel-an-expired-escrow @@ -3071,7 +3071,7 @@ code_of_conduct.ja: to: /ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/look-up-escrows type: 301 /ja/use-an-escrow-as-a-smart-contract.html: - to: /ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/use-an-escrow-as-a-smart-contract + to: /ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow type: 301 /ja/use-payment-channels.html: to: /ja/docs/tutorials/how-tos/use-specialized-payment-types/use-payment-channels diff --git a/sidebars.yaml b/sidebars.yaml index bb492584222..76b281e6426 100644 --- a/sidebars.yaml +++ b/sidebars.yaml @@ -300,10 +300,9 @@ expanded: false items: - page: docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md - - page: docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md + - page: docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow.md - page: docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/cancel-an-expired-escrow.md - page: docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/look-up-escrows.md - - page: docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/use-an-escrow-as-a-smart-contract.md - page: docs/tutorials/how-tos/use-specialized-payment-types/use-payment-channels/index.md expanded: false items: From 4154c671f673c0eec629b2681000322f8a594527 Mon Sep 17 00:00:00 2001 From: mDuo13 Date: Thu, 4 Dec 2025 15:48:57 -0800 Subject: [PATCH 09/12] Minor escrow tutorial edits --- .../how-tos/use-specialized-payment-types/use-escrows/index.md | 2 -- .../use-escrows/send-a-conditional-escrow.md | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/index.md b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/index.md index c5c2aa09ad5..f553c61d9f6 100644 --- a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/index.md +++ b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/index.md @@ -1,6 +1,4 @@ --- -html: use-escrows.html -parent: use-specialized-payment-types.html metadata: indexPage: true --- diff --git a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow.md b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow.md index d43b3fbed63..e936c9d8ffe 100644 --- a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow.md +++ b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow.md @@ -15,6 +15,7 @@ This tutorial shows how to escrow XRP. If the [TokenEscrow amendment][] is enabl By following this tutorial, you should learn how to: - Convert a timestamp into the XRP Ledger's native format. +- Create a crypto-condition and fulfillment in the format needed for the XRP Ledger. - Create and finish an escrow. ## Prerequisites From fbf3668e575337dcf066ef8797ada8ab7b65ab62 Mon Sep 17 00:00:00 2001 From: mDuo13 Date: Thu, 4 Dec 2025 18:06:25 -0800 Subject: [PATCH 10/12] Begin updating cancel escrow, & minor other edits --- .../escrow/js/send-conditional-escrow.js | 4 +- _code-samples/escrow/js/send-timed-escrow.js | 4 +- _code-samples/escrow/py/cancel_escrow.py | 135 +++++++++++++++--- .../escrow/py/send_conditional_escrow.py | 4 +- .../use-escrows/cancel-an-expired-escrow.md | 59 +++++++- redirects.yaml | 6 + 6 files changed, 184 insertions(+), 28 deletions(-) diff --git a/_code-samples/escrow/js/send-conditional-escrow.js b/_code-samples/escrow/js/send-conditional-escrow.js index 594b4d303b8..14c3fa9be25 100644 --- a/_code-samples/escrow/js/send-conditional-escrow.js +++ b/_code-samples/escrow/js/send-conditional-escrow.js @@ -7,10 +7,10 @@ await client.connect() console.log('Funding new wallet from faucet...') const { wallet } = await client.fundWallet() -const destination_address = 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe' // Testnet faucet +// const destination_address = 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe' // Testnet faucet // Alternative: Get another account to send the escrow to. Use this if you get // a tecDIR_FULL error trying to create escrows to the Testnet faucet. -// const destination_address = (await client.fundWallet()).wallet.address +const destination_address = (await client.fundWallet()).wallet.address // Create the crypto-condition for release ---------------------------------- const preimage = randomBytes(32) diff --git a/_code-samples/escrow/js/send-timed-escrow.js b/_code-samples/escrow/js/send-timed-escrow.js index 723d026741b..25117ecbc8a 100644 --- a/_code-samples/escrow/js/send-timed-escrow.js +++ b/_code-samples/escrow/js/send-timed-escrow.js @@ -5,10 +5,10 @@ await client.connect() console.log('Funding new wallet from faucet...') const { wallet } = await client.fundWallet() -const destination_address = 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe' // Testnet faucet +// const destination_address = 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe' // Testnet faucet // Alternative: Get another account to send the escrow to. Use this if you get // a tecDIR_FULL error trying to create escrows to the Testnet faucet. -// const destination_address = (await client.fundWallet()).wallet.address +const destination_address = (await client.fundWallet()).wallet.address // Set the escrow finish time ----------------------------------------------- const delay = 30 // Seconds in the future when the escrow should mature diff --git a/_code-samples/escrow/py/cancel_escrow.py b/_code-samples/escrow/py/cancel_escrow.py index 38cd32a88ca..6d154cde26e 100644 --- a/_code-samples/escrow/py/cancel_escrow.py +++ b/_code-samples/escrow/py/cancel_escrow.py @@ -1,31 +1,128 @@ +import json +from datetime import datetime, timedelta, UTC +from time import sleep + from xrpl.clients import JsonRpcClient -from xrpl.models import EscrowCancel +from xrpl.models import EscrowCreate, EscrowCancel +from xrpl.models.requests import AccountObjects, Ledger, Tx from xrpl.transaction import submit_and_wait +from xrpl.utils import datetime_to_ripple_time, ripple_time_to_datetime, get_balance_changes from xrpl.wallet import generate_faucet_wallet -client = JsonRpcClient("https://s.altnet.rippletest.net:51234") # Connect to the testnetwork +# Set up client and get a wallet +client = JsonRpcClient("https://s.altnet.rippletest.net:51234") +print("Funding new wallet from faucet...") +wallet = generate_faucet_wallet(client, debug=True) +# destination_address = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe" # Testnet faucet +# Alternative: Get another account to send the escrow to. Use this if you get +# a tecDIR_FULL error trying to create escrows to the Testnet faucet. +destination_address = generate_faucet_wallet(client, debug=True).address -# Cancel an escrow -# An Escrow can only be canceled if it was created with a CancelAfter time +# Create an escrow that won't be finished ----------------------------------- +cancel_delay = 30 +cancel_after = datetime.now(tz=UTC) + timedelta(seconds=cancel_delay) +print("This escrow will expire after", cancel_after) +cancel_after_rippletime = datetime_to_ripple_time(cancel_after) +# Use a crypto-condition that nobody knows the fulfillment for +condition_hex = "A02580200000000000000000000000000000000000000000000000000000000000000000810120" +escrow_create = EscrowCreate( + account=wallet.address, + destination=destination_address, + amount="123456", # drops of XRP + condition=condition_hex, + cancel_after=cancel_after_rippletime +) +print("Signing and submitting the EscrowCreate transaction.") +response = submit_and_wait(escrow_create, client, wallet, autofill=True) +print(json.dumps(response.result, indent=2)) -escrow_sequence = 30215126 +result_code = response.result["meta"]["TransactionResult"] +if result_code != "tesSUCCESS": + print(f"EscrowCreate failed with result code {result_code}") + exit(1) -# Sender wallet object -sender_wallet = generate_faucet_wallet(client=client) +# Wait for the escrow to be finishable -------------------------------------- +# Since ledger close times can be rounded by up to 10 seconds, wait an extra +# 10 seconds to make sure the escrow has officially expired. +sleep(cancel_delay + 10) -# Build escrow cancel transaction -cancel_txn = EscrowCancel( - account=sender_wallet.address, - owner=sender_wallet.address, - offer_sequence=escrow_sequence +# Look up the official close time of the validated ledger ------------------- +validated_ledger = client.request(Ledger(ledger_index="validated")) +close_time = validated_ledger.result["ledger"]["close_time"] +print("Latest validated ledger closed at", + ripple_time_to_datetime(close_time) ) +ledger_hash = validated_ledger.result["ledger"]["ledger_hash"] + +# Look up escrows connected to the account ---------------------------------- +expired_escrow = None +marker=None +while True: + try: + response = client.request(AccountObjects( + account=wallet.address, + ledger_hash=ledger_hash, + type="escrow", + marker=marker + )) + except Exception as e: + print(f"Error: account_objects failed: {e}") + exit(1) + + for escrow in response.result["account_objects"]: + if "CancelAfter" not in escrow: + print("This escrow does not have an expiration") + elif escrow["CancelAfter"] < close_time: + print("This escrow has expired.") + expired_escrow = escrow + break + else: + expiration_time = ripple_time_to_datetime(escrow["CancelAfter"]) + print(f"This escrow expires at {expiration_time}.") + + if "marker" in response.result.keys(): + marker=marker + else: + # This is the last page of results + break -# Autofill, sign, then submit transaction and wait for result -stxn_response = submit_and_wait(cancel_txn, client, sender_wallet) +if not expired_escrow: + print("Did not find any expired escrows.") + exit(1) + +# Find the sequence number of the expired escrow ---------------------------- +response = client.request(Tx(transaction=escrow["PreviousTxnID"])) +if not response.is_successful(): + print("Couldn't get transaction. Maybe this server doesn't have enough " + "transaction history available?") + exit(1) + +if response.result["tx_json"]["TransactionType"] == "EscrowCreate": + # Save this sequence number for canceling the escrow + escrow_seq = response.result["tx_json"]["Sequence"] +else: + # Currently, this is impossible since no current transaction can update + # an escrow without finishing or canceling it. But in the future, if + # that becomes possible, you would have to look at the transaction + # metadata to find the previous transaction and repeat until you found + # the transaction that created the escrow. + print("The escrow's previous transaction wasn't Create?!") + exit(1) + +# Send EscrowCancel transaction --------------------------------------------- +escrow_cancel = EscrowCancel( + account=wallet.address, + owner=expired_escrow["Account"], + offer_sequence=escrow_seq +) +print("Signing and submitting the EscrowCancel transaction.") +response2 = submit_and_wait(escrow_cancel, client, wallet, autofill=True) +print(json.dumps(response2.result, indent=2)) -# Parse response and return result -stxn_result = stxn_response.result +result_code = response2.result["meta"]["TransactionResult"] +if result_code != "tesSUCCESS": + print(f"EscrowCancel failed with result code {result_code}") + exit(1) -# Parse result and print out the transaction result and transaction hash -print(stxn_result["meta"]["TransactionResult"]) -print(stxn_result["hash"]) +print("Escrow canceled. Balance changes:") +print(json.dumps(get_balance_changes(response2.result["meta"]), indent=2)) diff --git a/_code-samples/escrow/py/send_conditional_escrow.py b/_code-samples/escrow/py/send_conditional_escrow.py index 9863d47394d..7b2b95691de 100644 --- a/_code-samples/escrow/py/send_conditional_escrow.py +++ b/_code-samples/escrow/py/send_conditional_escrow.py @@ -13,10 +13,10 @@ client = JsonRpcClient("https://s.altnet.rippletest.net:51234") print("Funding new wallet from faucet...") wallet = generate_faucet_wallet(client, debug=True) -destination_address = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe" # Testnet faucet +#destination_address = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe" # Testnet faucet # Alternative: Get another account to send the escrow to. Use this if you get # a tecDIR_FULL error trying to create escrows to the Testnet faucet. -# destination_address = generate_faucet_wallet(client, debug=True).address +destination_address = generate_faucet_wallet(client, debug=True).address # Create the crypto-condition for release ----------------------------------- preimage = urandom(32) diff --git a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/cancel-an-expired-escrow.md b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/cancel-an-expired-escrow.md index 9b11580aa0f..6e6105fec58 100644 --- a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/cancel-an-expired-escrow.md +++ b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/cancel-an-expired-escrow.md @@ -1,14 +1,67 @@ --- -html: cancel-an-expired-escrow.html -parent: use-escrows.html seo: description: Cancel an expired escrow. labels: - Escrow - - Smart Contracts --- # Cancel an Expired Escrow +This tutorial demonstrates how to cancel an [escrow](../../../../concepts/payment-types/escrow.md) that has passed its expiration time. You can use this to reclaim funds that you escrowed but were never claimed by the recipient. + +## Goals + +By following this tutorial, you should learn how to: + +- Compare a timestamp from the ledger to the current time. +- Cancel an expired escrow. + +## Prerequisites + +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. +- Already know how to send a [timed](./send-a-timed-escrow.md) or [conditional](./send-a-conditional-escrow.md) escrow. + +## Source Code + +You can find the complete source code for this tutorial's examples in the {% repo-link path="_code-samples/escrow/" %}code samples section of this website's repository{% /repo-link %}. + +## Steps + +### 1. Install dependencies + +{% tabs %} +{% tab label="JavaScript" %} +From the code sample folder, use `npm` to install dependencies: + +```sh +npm i +``` +{% /tab %} + +{% tab label="Python" %} +From the code sample folder, set up a virtual environment and use `pip` to install dependencies: + +```sh +python -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +``` +{% /tab %} +{% /tabs %} + +***TODO: finish refactoring*** + + + + + + + + + + An escrow in the XRP Ledger is expired when its `CancelAfter` time is lower than the `close_time` of the latest validated ledger. Escrows without a `CancelAfter` time never expire. ## 1. Get the latest validated ledger diff --git a/redirects.yaml b/redirects.yaml index 5544da1d4e3..ce9aabfb530 100644 --- a/redirects.yaml +++ b/redirects.yaml @@ -1,3 +1,6 @@ +/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/use-an-escrow-as-a-smart-contract: + to: /docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow + type: 301 /docs/infrastructure/installation/rippled-1-3-migration-instructions/: to: /docs/infrastructure/installation/ type: 301 @@ -2335,6 +2338,9 @@ code_of_conduct.ja: type: 301 # Japanese +/ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/use-an-escrow-as-a-smart-contract: + to: /ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow + type: 301 /ja/docs/infrastructure/installation/rippled-1-3-migration-instructions/: to: /ja/docs/infrastructure/installation/ type: 301 From 22c883fd673ea52fff5b6e90304240940f77360b Mon Sep 17 00:00:00 2001 From: mDuo13 Date: Fri, 5 Dec 2025 16:52:40 -0800 Subject: [PATCH 11/12] Update cancel escrow tutorial w/ rewritten (Python) sample code --- .../use-escrows/cancel-an-expired-escrow.md | 119 ----------------- .../account_objects-request-expiration.json | 7 - .../account_objects-response-expiration.json | 26 ---- .../websocket/ledger-request-expiration.json | 5 - .../websocket/ledger-response-expiration.json | 19 --- .../submit-request-escrowcancel.json | 11 -- .../submit-response-escrowcancel.json | 23 ---- .../websocket/tx-request-escrowcancel.json | 5 - .../websocket/tx-response-escrowcancel.json | 101 -------------- _code-samples/escrow/js/finish-escrow.js | 60 --------- _code-samples/escrow/js/makecondition.js | 9 -- _code-samples/escrow/js/send-timed-escrow.js | 5 +- _code-samples/escrow/py/cancel_escrow.py | 7 +- _code-samples/escrow/py/finish_escrow.py | 44 ------- .../escrow/py/return_escrow_sequence.py | 24 ---- .../escrow/py/send_conditional_escrow.py | 2 +- .../use-escrows/cancel-an-expired-escrow.md | 123 ++++++------------ .../use-escrows/send-a-conditional-escrow.md | 2 +- .../use-escrows/send-a-timed-escrow.md | 6 +- 19 files changed, 57 insertions(+), 541 deletions(-) delete mode 100644 @l10n/ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/cancel-an-expired-escrow.md delete mode 100644 _api-examples/escrow/websocket/account_objects-request-expiration.json delete mode 100644 _api-examples/escrow/websocket/account_objects-response-expiration.json delete mode 100644 _api-examples/escrow/websocket/ledger-request-expiration.json delete mode 100644 _api-examples/escrow/websocket/ledger-response-expiration.json delete mode 100644 _api-examples/escrow/websocket/submit-request-escrowcancel.json delete mode 100644 _api-examples/escrow/websocket/submit-response-escrowcancel.json delete mode 100644 _api-examples/escrow/websocket/tx-request-escrowcancel.json delete mode 100644 _api-examples/escrow/websocket/tx-response-escrowcancel.json delete mode 100644 _code-samples/escrow/js/finish-escrow.js delete mode 100644 _code-samples/escrow/js/makecondition.js delete mode 100644 _code-samples/escrow/py/finish_escrow.py delete mode 100644 _code-samples/escrow/py/return_escrow_sequence.py diff --git a/@l10n/ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/cancel-an-expired-escrow.md b/@l10n/ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/cancel-an-expired-escrow.md deleted file mode 100644 index fc730ee26d9..00000000000 --- a/@l10n/ja/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/cancel-an-expired-escrow.md +++ /dev/null @@ -1,119 +0,0 @@ ---- -html: cancel-an-expired-escrow.html -parent: use-escrows.html -seo: - description: 有効期限切れのEscrowを取り消します。 -labels: - - Escrow - - スマートコントラクト ---- -# 有効期限切れEscrowの取消し - -## 1.有効期限切れEscrowの確認 - -XRP LedgerのEscrowが有効期限切れとなるのは、その`CancelAfter`の時刻が検証済みレジャーの`close_time`よりも前である場合です。(Escrowに`CancelAfter`時刻が指定されていない場合は、Escrowが有効期限切れになることはありません。)最新の検証済みレジャーの閉鎖時刻は、[ledgerメソッド][]を使用して検索できます。 - -リクエスト: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/ledger-request-expiration.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -レスポンス: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/ledger-response-expiration.json" language="json" /%} -{% /tab %} - -{% /tabs %} - - -[account_objectsメソッド][]を使用してEscrowを検索し、`CancelAfter`の時刻と比較できます。 - -リクエスト: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/account_objects-request-expiration.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -レスポンス: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/account_objects-response-expiration.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -## 2.EscrowCancelトランザクションの送信 - -XRP Ledgerでは、[EscrowCancelトランザクション][]に[署名して送信する](../../../../concepts/transactions/index.md#トランザクションへの署名とトランザクションの送信)ことで、***誰でも***有効期限切れのEscrowを取り消すことができます。トランザクションの`Owner`フィールドを、そのEscrowを作成した`EscrowCreate`トランザクションの`Account`に設定します。`OfferSequence`フィールドを、`EscrowCreate`トランザクションの`Sequence`に設定します。 - -{% partial file="/@l10n/ja/docs/_snippets/secret-key-warning.md" /%} - -リクエスト: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/submit-request-escrowcancel.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -レスポンス: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/submit-response-escrowcancel.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -トランザクションの識別用`hash`値をメモしておきます。これにより、検証済みレジャーバージョンに記録されるときにその最終ステータスを確認できます。 - -## 3.検証の待機 - -{% partial file="/@l10n/ja/docs/_snippets/wait-for-validation.md" /%} - -## 4.最終結果の確認 - -EscrowCancelトランザクションの識別用ハッシュを指定した[txメソッド][]を使用してトランザクションの最終ステータスを確認します。トランザクションのメタデータで`LedgerEntryType`が`Escrow`である`DeletedNode`を探します。また、エスクローに預託された支払いの送金元の`ModifiedNode`(タイプが`AccountRoot`)も探します。オブジェクトの`FinalFields`に、`Balance`フィールドのXRP返金額の増分が表示されている必要があります。 - -リクエスト: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/tx-request-escrowcancel.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -レスポンス: - -{% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/tx-response-escrowcancel.json" language="json" /%} -{% /tab %} - -{% /tabs %} - -上記の例では、`r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT`がEscrowの送金元であり、`Balance`が99999**8**9990 dropから99999**9**9990 dropに増加していることから、エスクローに預託されていた10,000 XRP dropが返金されたことがわかります(drop = 0.01XRP) 。 - -{% admonition type="success" name="ヒント" %}Escrowを実行する[EscrowFinishトランザクション][]で使用する`OfferSequence`が不明な場合は、Escrowの`PreviousTxnID`フィールドのトランザクションの識別用ハッシュを指定した[txメソッド][]を使用して、そのEscrowを作成したトランザクションを検索します。Escrowを終了するときには、そのトランザクションの`Sequence`の値を`OfferSequence`の値として使用します。{% /admonition %} - -{% raw-partial file="/@l10n/ja/docs/_snippets/common-links.md" /%} diff --git a/_api-examples/escrow/websocket/account_objects-request-expiration.json b/_api-examples/escrow/websocket/account_objects-request-expiration.json deleted file mode 100644 index 5f998c30743..00000000000 --- a/_api-examples/escrow/websocket/account_objects-request-expiration.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "id": 2, - "command": "account_objects", - "account": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT", - "ledger_index": "validated", - "type": "escrow" -} diff --git a/_api-examples/escrow/websocket/account_objects-response-expiration.json b/_api-examples/escrow/websocket/account_objects-response-expiration.json deleted file mode 100644 index ea01415a178..00000000000 --- a/_api-examples/escrow/websocket/account_objects-response-expiration.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id": 2, - "status": "success", - "type": "response", - "result": { - "account": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT", - "account_objects": [ - { - "Account": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT", - "Amount": "10000", - "CancelAfter": 559913895, - "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", - "FinishAfter": 559892324, - "Flags": 0, - "LedgerEntryType": "Escrow", - "OwnerNode": "0000000000000000", - "PreviousTxnID": "4756C22BBB7FC23D9081FDB180806939D6FEBC967BE0EC2DB95B166AF9C086E9", - "PreviousTxnLgrSeq": 2764813, - "index": "7243A9750FA4BE3E63F75F6DACFD79AD6B6C76947F6BDC46CD0F52DBEEF64C89" - } - ], - "ledger_hash": "82F24FFA72AED16F467BBE79D387E92FDA39F29038B26E79464CDEDFB506E366", - "ledger_index": 2764826, - "validated": true - } -} diff --git a/_api-examples/escrow/websocket/ledger-request-expiration.json b/_api-examples/escrow/websocket/ledger-request-expiration.json deleted file mode 100644 index fe3c8e9f55f..00000000000 --- a/_api-examples/escrow/websocket/ledger-request-expiration.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "id": 4, - "command": "ledger", - "ledger_index": "validated" -} diff --git a/_api-examples/escrow/websocket/ledger-response-expiration.json b/_api-examples/escrow/websocket/ledger-response-expiration.json deleted file mode 100644 index 286e8ddac78..00000000000 --- a/_api-examples/escrow/websocket/ledger-response-expiration.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "id": 1, - "status": "success", - "type": "response", - "result": { - "ledger": { - # ... (trimmed) ... - - "close_time": 560302643, - "close_time_human": "2017-Oct-02 23:37:23", - "close_time_resolution": 10, - - # ... (trimmed) ... - }, - "ledger_hash": "668F0647A6F3CC277496245DBBE9BD2E3B8E70E7AA824E97EF3237FE7E1EE3F2", - "ledger_index": 2906341, - "validated": true - } -} diff --git a/_api-examples/escrow/websocket/submit-request-escrowcancel.json b/_api-examples/escrow/websocket/submit-request-escrowcancel.json deleted file mode 100644 index 47dd33a56d9..00000000000 --- a/_api-examples/escrow/websocket/submit-request-escrowcancel.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "id": 5, - "command": "submit", - "secret": "s████████████████████████████", - "tx_json": { - "Account": "rhgdnc82FwHFUKXp9ZcpgwXWRAxKf5Buqp", - "TransactionType": "EscrowCancel", - "Owner": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT", - "OfferSequence": 1 - } -} diff --git a/_api-examples/escrow/websocket/submit-response-escrowcancel.json b/_api-examples/escrow/websocket/submit-response-escrowcancel.json deleted file mode 100644 index 9e7a7c0fe3d..00000000000 --- a/_api-examples/escrow/websocket/submit-response-escrowcancel.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "id": 5, - "status": "success", - "type": "response", - "result": { - "engine_result": "tesSUCCESS", - "engine_result_code": 0, - "engine_result_message": "The transaction was applied. Only final in a validated ledger.", - "tx_blob": "1200042280000000240000000320190000000168400000000000000A7321027FB1CF34395F18901CD294F77752EEE25277C6E87A224FC7388AA7EF872DB43D74473045022100AC45749FC4291F7811B2D8AC01CA04FEE38910CB7216FB0C5C0AEBC9C0A95F4302203F213C71C00136A0ADC670EFE350874BCB2E559AC02059CEEDFB846685948F2B81142866B7B47574C8A70D5E71FFB95FFDB18951427B82144E87970CD3EA984CF48B1AA6AB6C77DC4AB059FC", - "tx_json": { - "Account": "rhgdnc82FwHFUKXp9ZcpgwXWRAxKf5Buqp", - "Fee": "10", - "Flags": 2147483648, - "OfferSequence": 1, - "Owner": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT", - "Sequence": 3, - "SigningPubKey": "027FB1CF34395F18901CD294F77752EEE25277C6E87A224FC7388AA7EF872DB43D", - "TransactionType": "EscrowCancel", - "TxnSignature": "3045022100AC45749FC4291F7811B2D8AC01CA04FEE38910CB7216FB0C5C0AEBC9C0A95F4302203F213C71C00136A0ADC670EFE350874BCB2E559AC02059CEEDFB846685948F2B", - "hash": "65F36C5514153D94F0ADE5CE747061A5E70B73B56B4C66DA5040D99CAF252831" - } - } -} diff --git a/_api-examples/escrow/websocket/tx-request-escrowcancel.json b/_api-examples/escrow/websocket/tx-request-escrowcancel.json deleted file mode 100644 index 25e1cdcdf2a..00000000000 --- a/_api-examples/escrow/websocket/tx-request-escrowcancel.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "id": 6, - "command": "tx", - "transaction": "65F36C5514153D94F0ADE5CE747061A5E70B73B56B4C66DA5040D99CAF252831" -} diff --git a/_api-examples/escrow/websocket/tx-response-escrowcancel.json b/_api-examples/escrow/websocket/tx-response-escrowcancel.json deleted file mode 100644 index 084c5ef7102..00000000000 --- a/_api-examples/escrow/websocket/tx-response-escrowcancel.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "id": 6, - "status": "success", - "type": "response", - "result": { - "Account": "rhgdnc82FwHFUKXp9ZcpgwXWRAxKf5Buqp", - "Fee": "10", - "Flags": 2147483648, - "OfferSequence": 1, - "Owner": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT", - "Sequence": 3, - "SigningPubKey": "027FB1CF34395F18901CD294F77752EEE25277C6E87A224FC7388AA7EF872DB43D", - "TransactionType": "EscrowCancel", - "TxnSignature": "3045022100AC45749FC4291F7811B2D8AC01CA04FEE38910CB7216FB0C5C0AEBC9C0A95F4302203F213C71C00136A0ADC670EFE350874BCB2E559AC02059CEEDFB846685948F2B", - "date": 560302841, - "hash": "65F36C5514153D94F0ADE5CE747061A5E70B73B56B4C66DA5040D99CAF252831", - "inLedger": 2906406, - "ledger_index": 2906406, - "meta": { - "AffectedNodes": [ - { - "ModifiedNode": { - "LedgerEntryType": "AccountRoot", - "LedgerIndex": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8", - "PreviousTxnID": "4756C22BBB7FC23D9081FDB180806939D6FEBC967BE0EC2DB95B166AF9C086E9", - "PreviousTxnLgrSeq": 2764813 - } - }, - { - "ModifiedNode": { - "FinalFields": { - "Account": "rhgdnc82FwHFUKXp9ZcpgwXWRAxKf5Buqp", - "Balance": "9999999970", - "Flags": 0, - "OwnerCount": 0, - "Sequence": 4 - }, - "LedgerEntryType": "AccountRoot", - "LedgerIndex": "3430FA3A160FA8F9842FA4A8B5549ECDCB3783E585D0F9796A1736DEAE35F6FE", - "PreviousFields": { - "Balance": "9999999980", - "Sequence": 3 - }, - "PreviousTxnID": "DA6F5CA8CE13A03B8BC58515E085F2FEF90B3C08230B5AEC8DE4FAF39F79010B", - "PreviousTxnLgrSeq": 2906391 - } - }, - { - "DeletedNode": { - "FinalFields": { - "Account": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT", - "Amount": "10000", - "CancelAfter": 559913895, - "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", - "FinishAfter": 559892324, - "Flags": 0, - "OwnerNode": "0000000000000000", - "PreviousTxnID": "4756C22BBB7FC23D9081FDB180806939D6FEBC967BE0EC2DB95B166AF9C086E9", - "PreviousTxnLgrSeq": 2764813 - }, - "LedgerEntryType": "Escrow", - "LedgerIndex": "7243A9750FA4BE3E63F75F6DACFD79AD6B6C76947F6BDC46CD0F52DBEEF64C89" - } - }, - { - "ModifiedNode": { - "FinalFields": { - "Flags": 0, - "Owner": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT", - "RootIndex": "DACDBEBD31D14EAC4207A45DB88734AD14D26D908507F41D2FC623BDD91C582F" - }, - "LedgerEntryType": "DirectoryNode", - "LedgerIndex": "DACDBEBD31D14EAC4207A45DB88734AD14D26D908507F41D2FC623BDD91C582F" - } - }, - { - "ModifiedNode": { - "FinalFields": { - "Account": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT", - "Balance": "9999999990", - "Flags": 0, - "OwnerCount": 0, - "Sequence": 2 - }, - "LedgerEntryType": "AccountRoot", - "LedgerIndex": "F5F1834B80A8B5DA878270AB4DE4EA444281181349375F1D21E46D5F3F0ABAC8", - "PreviousFields": { - "Balance": "9999989990", - "OwnerCount": 1 - }, - "PreviousTxnID": "4756C22BBB7FC23D9081FDB180806939D6FEBC967BE0EC2DB95B166AF9C086E9", - "PreviousTxnLgrSeq": 2764813 - } - } - ], - "TransactionIndex": 2, - "TransactionResult": "tesSUCCESS" - }, - "validated": true - } -} diff --git a/_code-samples/escrow/js/finish-escrow.js b/_code-samples/escrow/js/finish-escrow.js deleted file mode 100644 index 879aedbf0dc..00000000000 --- a/_code-samples/escrow/js/finish-escrow.js +++ /dev/null @@ -1,60 +0,0 @@ -'use strict' -const xrpl = require('xrpl') - -// Preqrequisites: -// 1. Create an escrow using the create-escrow.js snippet -// 2. Replace the OfferSequence with the sequence number of the escrow you created -// 3. Replace the Condition and Fulfillment with the values from the escrow you created -// 4. Paste the seed of the account that created the escrow -// 5. Run the snippet - -const seed = "sEd7jfWyNG6J71dEojB3W9YdHp2KCjy"; // Test seed. Don't use -const offerSequence = null; -const condition = ""; -const fulfillment = ""; - -const main = async () => { - try { - // Connect ---------------------------------------------------------------- - const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233'); - await client.connect(); - - // Prepare wallet to sign the transaction --------------------------------- - const wallet = await xrpl.Wallet.fromSeed(seed); - console.log("Wallet Address: ", wallet.address); - console.log("Seed: ", seed); - - if((!offerSequence)|| (condition === "" || fulfillment === "")){ - throw new Error("Please specify the sequence number, condition and fulfillment of the escrow you created"); - }; - - // Prepare EscrowFinish transaction --------------------------------- - const escrowFinishTransaction = { - "Account": wallet.address, - "TransactionType": "EscrowFinish", - "Owner": wallet.address, - // This should equal the sequence number of the escrow transaction - "OfferSequence": offerSequence, - // Crypto condition that must be met before escrow can be completed, passed on escrow creation. - // Omit this for time-held escrows. - "Condition": condition, - // Fulfillment of the condition, passed on escrow creation. - // Omit this for time-held escrows. - "Fulfillment": fulfillment, - }; - - xrpl.validate(escrowFinishTransaction); - - // Sign and submit the transaction ---------------------------------------- - console.log('Signing and submitting the transaction:', JSON.stringify(escrowFinishTransaction, null, "\t")); - const response = await client.submitAndWait(escrowFinishTransaction, { wallet }); - console.log(`Finished submitting! ${JSON.stringify(response.result, null, "\t")}`); - - await client.disconnect(); - - } catch (error) { - console.log(error); - } -} - -main() diff --git a/_code-samples/escrow/js/makecondition.js b/_code-samples/escrow/js/makecondition.js deleted file mode 100644 index ea9f192564f..00000000000 --- a/_code-samples/escrow/js/makecondition.js +++ /dev/null @@ -1,9 +0,0 @@ -const cc = require('five-bells-condition') -const crypto = require('crypto') - -const preimageData = crypto.randomBytes(32) -const myFulfillment = new cc.PreimageSha256() -myFulfillment.setPreimage(preimageData) - -console.log('Condition:', myFulfillment.getConditionBinary().toString('hex').toUpperCase()) -console.log('Fulfillment:', myFulfillment.serializeBinary().toString('hex').toUpperCase()) diff --git a/_code-samples/escrow/js/send-timed-escrow.js b/_code-samples/escrow/js/send-timed-escrow.js index 25117ecbc8a..00f0e60b4a1 100644 --- a/_code-samples/escrow/js/send-timed-escrow.js +++ b/_code-samples/escrow/js/send-timed-escrow.js @@ -98,7 +98,10 @@ const response2 = await client.submitAndWait(escrowFinish, { }) console.log(JSON.stringify(response2.result, null, 2)) if (response2.result.meta.TransactionResult === 'tesSUCCESS') { - console.log('Escrow finished successfully.') + console.log('Escrow finished successfully. Balance changes:') + console.log( + JSON.stringify(xrpl.getBalanceChanges(response2.result.meta), null, 2) + ) } client.disconnect() diff --git a/_code-samples/escrow/py/cancel_escrow.py b/_code-samples/escrow/py/cancel_escrow.py index 6d154cde26e..ed0b064599f 100644 --- a/_code-samples/escrow/py/cancel_escrow.py +++ b/_code-samples/escrow/py/cancel_escrow.py @@ -41,7 +41,7 @@ print(f"EscrowCreate failed with result code {result_code}") exit(1) -# Wait for the escrow to be finishable -------------------------------------- +# Wait for the escrow to expire --------------------------------------------- # Since ledger close times can be rounded by up to 10 seconds, wait an extra # 10 seconds to make sure the escrow has officially expired. sleep(cancel_delay + 10) @@ -100,13 +100,16 @@ if response.result["tx_json"]["TransactionType"] == "EscrowCreate": # Save this sequence number for canceling the escrow escrow_seq = response.result["tx_json"]["Sequence"] + if escrow_seq == 0: + # This transaction used a Ticket, so use the TicketSequence instead. + escrow_seq = response.result["tx_json"]["TicketSequence"] else: # Currently, this is impossible since no current transaction can update # an escrow without finishing or canceling it. But in the future, if # that becomes possible, you would have to look at the transaction # metadata to find the previous transaction and repeat until you found # the transaction that created the escrow. - print("The escrow's previous transaction wasn't Create?!") + print("The escrow's previous transaction wasn't EscrowCreate!") exit(1) # Send EscrowCancel transaction --------------------------------------------- diff --git a/_code-samples/escrow/py/finish_escrow.py b/_code-samples/escrow/py/finish_escrow.py deleted file mode 100644 index f4d58a3f117..00000000000 --- a/_code-samples/escrow/py/finish_escrow.py +++ /dev/null @@ -1,44 +0,0 @@ -from xrpl.clients import JsonRpcClient -from xrpl.models import EscrowFinish -from xrpl.transaction import submit_and_wait -from xrpl.wallet import generate_faucet_wallet - -client = JsonRpcClient("https://s.altnet.rippletest.net:51234") # Connect to the testnetwork - -# Complete an escrow -# Cannot be called until the finish time is reached - -# Required fields (modify to match an escrow you create) -escrow_creator = generate_faucet_wallet(client=client).address - -escrow_sequence = 27641268 - -# Optional fields - -# Crypto condition that must be met before escrow can be completed, passed on escrow creation -condition = "A02580203882E2EB9B44130530541C4CC360D079F265792C4A7ED3840968897CB7DF2DA1810120" - -# Crypto fulfillment of the condtion -fulfillment = "A0228020AED2C5FE4D147D310D3CFEBD9BFA81AD0F63CE1ADD92E00379DDDAF8E090E24C" - -# Sender wallet object -sender_wallet = generate_faucet_wallet(client=client) - -# Build escrow finish transaction -finish_txn = EscrowFinish( - account=sender_wallet.address, - owner=escrow_creator, - offer_sequence=escrow_sequence, # The sequence number of the escrow transaction - condition=condition, # Omit this for time-held escrows - fulfillment=fulfillment # Omit this for time-held escrows -) - -# Autofill, sign, then submit transaction and wait for result -stxn_response = submit_and_wait(finish_txn, client, sender_wallet) - -# Parse response and return result -stxn_result = stxn_response.result - -# Parse result and print out the transaction result and transaction hash -print(stxn_result["meta"]["TransactionResult"]) -print(stxn_result["hash"]) diff --git a/_code-samples/escrow/py/return_escrow_sequence.py b/_code-samples/escrow/py/return_escrow_sequence.py deleted file mode 100644 index f3dffa15229..00000000000 --- a/_code-samples/escrow/py/return_escrow_sequence.py +++ /dev/null @@ -1,24 +0,0 @@ -from xrpl.clients import JsonRpcClient -from xrpl.models import Tx - -client = JsonRpcClient("https://s.altnet.rippletest.net:51234") # Connect to the testnetwork - - -prev_txn_id = "" # should look like this '84503EA84ADC4A65530C6CC91C904FCEE64CFE2BB973C023476184288698991F' -# Return escrow seq from `PreviousTxnID` for finishing or cancelling escrows -if prev_txn_id == "": - print("No transaction id provided. Use create_escrow.py to generate an escrow transaction, then you can look it up by modifying prev_txn_id to use that transaction's id.") - -# Build and send query for PreviousTxnID -req = Tx(transaction=prev_txn_id) -response = client.request(req) - -# Return the result -result = response.result - -# Print escrow sequence if available -if "Sequence" in result: - print(f'escrow sequence: {result["Sequence"]}') -# Use escrow ticket sequence if escrow sequence is not available -if "TicketSequence" in result: - print(f'escrow ticket sequence: {result["TicketSequence"]}') diff --git a/_code-samples/escrow/py/send_conditional_escrow.py b/_code-samples/escrow/py/send_conditional_escrow.py index 7b2b95691de..358e2ade295 100644 --- a/_code-samples/escrow/py/send_conditional_escrow.py +++ b/_code-samples/escrow/py/send_conditional_escrow.py @@ -54,7 +54,7 @@ # Save the sequence number so you can identify the escrow later escrow_seq = response.result["tx_json"]["Sequence"] -# Send the EscrowFinish transaction ----------------------------------------- +# Send EscrowFinish transaction --------------------------------------------- escrow_finish = EscrowFinish( account=wallet.address, owner=wallet.address, diff --git a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/cancel-an-expired-escrow.md b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/cancel-an-expired-escrow.md index 6e6105fec58..81022b4340b 100644 --- a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/cancel-an-expired-escrow.md +++ b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/cancel-an-expired-escrow.md @@ -6,7 +6,7 @@ labels: --- # Cancel an Expired Escrow -This tutorial demonstrates how to cancel an [escrow](../../../../concepts/payment-types/escrow.md) that has passed its expiration time. You can use this to reclaim funds that you escrowed but were never claimed by the recipient. +This tutorial demonstrates how to cancel an [escrow](../../../../concepts/payment-types/escrow.md) that has passed its expiration time. You can use this to reclaim funds that you escrowed but were never claimed by the recipient, or to remove an expired escrow that is stopping you from deleting your account. ## Goals @@ -51,134 +51,92 @@ pip install -r requirements.txt {% /tab %} {% /tabs %} -***TODO: finish refactoring*** +### 2. Set up client and account - - - - - - - - - -An escrow in the XRP Ledger is expired when its `CancelAfter` time is lower than the `close_time` of the latest validated ledger. Escrows without a `CancelAfter` time never expire. - -## 1. Get the latest validated ledger - -Use the [ledger method][] to look up the latest validated ledger and get the `close_time` value. - -Request: +To get started, import the client library and instantiate an API client. For this tutorial, you also need one account, which you can get from the faucet. You also need the address of another account to send the escrow to. You can fund a second account using the faucet, or use the address of an existing account like the faucet. {% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/ledger-request-expiration.json" language="json" /%} +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/cancel_escrow.py" language="py" before="# Create an escrow" /%} {% /tab %} - {% /tabs %} -Response: +### 3. Create an escrow -{% tabs %} +For purposes of this tutorial, you need an escrow to cancel, so create one that won't be finished before it expires. The sample code uses a conditional escrow with a made-up condition full of zeroes, so nobody knows the fulfillment, and an expiration time 30 seconds into the future. A timed escrow could also work, but it's possible someone else would finish the escrow between its maturity and expiration time. -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/ledger-response-expiration.json" language="json" /%} -{% /tab %} +Anyone can cancel _any_ expired escrow; you don't have to be the sender or receiver. That said, the sender has the most financial incentive to do so, since they get the funds back. +{% tabs %} +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/cancel_escrow.py" language="py" from="# Create an escrow" before="# Wait for the escrow to expire" /%} +{% /tab %} {% /tabs %} -## 2. Look up the escrow +{% admonition type="success" name="Tip" %}For a more detailed explanation of creating an escrow, see [Send a Timed Escrow](./send-a-timed-escrow.md) or [Send a Conditional Escrow](./send-a-conditional-escrow.md).{% /admonition %} -Use the [account_objects method][] and compare `CancelAfter` to `close_time`: +### 4. Wait for the escrow to expire -Request: +An escrow can only be canceled after it has expired, so you have to wait until its `CancelAfter` (expiration) time has passed. Since the expiration time is compared to the official close time of the previous ledger, which may be rounded up to 10 seconds, waiting an extra 10 seconds makes it very likely that the escrow has officially expired. {% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/account_objects-request-expiration.json" language="json" /%} +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/cancel_escrow.py" language="py" from="# Wait for the escrow to expire" before="# Look up the official close time" /%} {% /tab %} - {% /tabs %} -Response: +### 5. Look up the official close time of the latest validated ledger -{% tabs %} +Use the [ledger method][] to get the official close time of the most recently validated ledger version. You can use this number to confirm when an escrow has officially expired. -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/account_objects-response-expiration.json" language="json" /%} +{% tabs %} +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/cancel_escrow.py" language="py" from="# Look up the official close time" before="# Look up escrows" /%} {% /tab %} - {% /tabs %} -## 3. Submit EscrowCancel transaction - -***Anyone*** can cancel an expired escrow in the XRP Ledger by sending an [EscrowCancel transaction][]. Set the `Owner` field of the transaction to the `Account` of the `EscrowCreate` transaction that created this escrow. Set the `OfferSequence` field to the `Sequence` of the `EscrowCreate` transaction. - -{% admonition type="success" name="Tip" %}If you don't know what `OfferSequence` to use, you can look up the transaction that created the Escrow: call the [tx method][] with the value of the Escrow's `PreviousTxnID` field. In `tx` response, use the `Sequence` value of that transaction as the `OfferSequence` value of the EscrowCancel transaction.{% /admonition %} +### 6. Look for expired escrows by account -{% partial file="/docs/_snippets/secret-key-warning.md" /%} +This is one of several ways to find expired escrows. Use the [account_objects method][] to look up escrows linked to your account. (This includes both incoming and outgoing escrows, potentially.) You may need to look through multiple [paginated][Marker] results if you have a lot of objects linked to your account. {% tabs %} - -{% tab label="Websocket" %} -Request: -{% code-snippet file="/_api-examples/escrow/websocket/submit-request-escrowcancel.json" language="json" /%} - -Response: -{% code-snippet file="/_api-examples/escrow/websocket/submit-response-escrowcancel.json" language="json" /%} -{% /tab %} - -{% tab label="Javascript" %} -{% code-snippet file="/_code-samples/escrow/js/cancel-escrow.js" language="js" from="const escrowCancelTransaction" before="await client.disconnect" /%} -{% /tab %} - {% tab label="Python" %} -{% code-snippet file="/_code-samples/escrow/py/cancel_escrow.py" language="py" from="# Build escrow cancel" /%} +{% code-snippet file="/_code-samples/escrow/py/cancel_escrow.py" language="py" from="# Look up escrows" before="# Find the sequence number" /%} {% /tab %} - {% /tabs %} -Take note of the transaction's identifying `hash` value so you can check its final status when it is included in a validated ledger version. - -## 4. Wait for validation +### 7. Find the sequence number of the expired escrow -{% raw-partial file="/docs/_snippets/wait-for-validation.md" /%} +To cancel an escrow, you need to know its owner and the sequence number of the transaction that created it. If you already know the sequence number (for example, you saved it when you created the escrow) you can skip this step. The sample code shows how you can look it up for an unknown escrow using the escrow ledger entry's transaction history. -## 5. Confirm final result +The `PreviousTxnID` field contains the identifying hash of the last transaction to modify the escrow. Generally, this is the EscrowCreate transaction, so you can look up that transaction, using the [tx method][] to get the sequence number from the `Sequence` field. If the transaction used a [Ticket](../../../../concepts/accounts/tickets.md), then the `Sequence` field has a value of `0` and you need to use value of the `TicketSequence` field instead. -Use the [tx method][] with the `EscrowCancel` transaction's identifying hash to check its final status. Look in the transaction metadata for a `DeletedNode` with `LedgerEntryType` of `Escrow`. Also look for a `ModifiedNode` of type `AccountRoot` for the sender of the escrowed payment. The `FinalFields` of the object should show the increase in XRP in the `Balance` field for the returned XRP. +{% admonition type="success" name="Tip" %}The `IncludeKeyletFields` amendment, expected to be released in `rippled` 3.0.0, adds a `Sequence` field to new Escrow ledger entries. For any escrow created after that amendment goes live, you can get the sequence number directly from that field.{% /admonition %} -Request: +In the case that the previous transaction is not an EscrowCreate transaction, you can use _that_ transaction's metadata to find the prior value of the same escrow's `PreviousTxnID`, and repeat the process until you find the actual EscrowCreate. In the current XRP Ledger protocol (as of late 2025), this case is extremely rare to impossible, so the sample code does not demonstrate this process. {% tabs %} - -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/tx-request-escrowcancel.json" language="json" /%} +{% tab label="Python" %} +{% code-snippet file="/_code-samples/escrow/py/cancel_escrow.py" language="py" from="# Find the sequence number" before="# Send EscrowCancel transaction" /%} {% /tab %} - {% /tabs %} -Response: +### 8. Cancel the escrow + +Once you have all the necessary information, send an [EscrowCancel transaction][] to cancel the escrow. If the transaction succeeds, it deletes the escrow entry and returns the escrowed funds to their source. {% tabs %} +{% tab label="Python" %} +In xrpl-py, you can use the [`get_balance_changes(metadata)`](https://xrpl-py.readthedocs.io/en/stable/source/xrpl.utils.html#xrpl.utils.get_balance_changes) utility to parse the validated transaction's metadata for a simplified list of balance changes. -{% tab label="Websocket" %} -{% code-snippet file="/_api-examples/escrow/websocket/tx-response-escrowcancel.json" language="json" /%} +{% code-snippet file="/_code-samples/escrow/py/cancel_escrow.py" language="py" from="# Send EscrowCancel transaction" /%} {% /tab %} - {% /tabs %} -In the above example, `r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT` is the sender of the escrow, and the increase in `Balance` from 99999**8**9990 drops to 99999**9**9990 drops represents the return of the escrowed 10,000 drops of XRP (0.01 XRP). - - ## See Also - **Concepts:** - - [What is XRP?](../../../../introduction/what-is-xrp.md) - - [Payment Types](../../../../concepts/payment-types/index.md) - - [Escrow](../../../../concepts/payment-types/escrow.md) + - [Escrow](../../../../concepts/payment-types/escrow.md) - **Tutorials:** - [Send XRP](../../send-xrp.md) - [Look Up Transaction Results](../../../../concepts/transactions/finality-of-results/look-up-transaction-results.md) @@ -188,7 +146,8 @@ In the above example, `r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT` is the sender of the - [EscrowCreate transaction][] - [EscrowFinish transaction][] - [account_objects method][] + - [ledger method][] - [tx method][] - - [Escrow ledger object](../../../../references/protocol/ledger-data/ledger-entry-types/escrow.md) + - [Escrow ledger entry](../../../../references/protocol/ledger-data/ledger-entry-types/escrow.md) {% raw-partial file="/docs/_snippets/common-links.md" /%} diff --git a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow.md b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow.md index e936c9d8ffe..7d867e32175 100644 --- a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow.md +++ b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow.md @@ -22,7 +22,7 @@ By following this tutorial, you should learn how to: To complete this tutorial, you should: -- Have a basic understanding of the XRP Ledger +- Have a basic understanding of the XRP Ledger. - Have an XRP Ledger client library, such as **xrpl.js**, installed. ## Source Code diff --git a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md index 30ff53c17ac..46ab59d3e7e 100644 --- a/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md +++ b/docs/tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-timed-escrow.md @@ -21,7 +21,7 @@ By following this tutorial, you should learn how to: To complete this tutorial, you should: -- Have a basic understanding of the XRP Ledger +- Have a basic understanding of the XRP Ledger. - Have an XRP Ledger client library, such as **xrpl.js**, installed. ## Source Code @@ -154,10 +154,14 @@ Now that the escrow is mature, you can finish it. Construct an [EscrowFinish tra {% tabs %} {% tab label="JavaScript" %} +In xrpl.js, you can use the [`getBalanceChanges(metadata)`](https://js.xrpl.org/functions/getBalanceChanges.html) utility to parse the validated transaction's metadata for a simplified list of balance changes. + {% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="// Send EscrowFinish transaction" /%} {% /tab %} {% tab label="Python" %} +In xrpl-py, you can use the [`get_balance_changes(metadata)`](https://xrpl-py.readthedocs.io/en/stable/source/xrpl.utils.html#xrpl.utils.get_balance_changes) utility to parse the validated transaction's metadata for a simplified list of balance changes. + {% code-snippet file="/_code-samples/escrow/py/send_timed_escrow.py" language="py" from="# Send EscrowFinish transaction" /%} {% /tab %} {% /tabs %} From e871db3940894368bca82e90567f47fa5aed2201 Mon Sep 17 00:00:00 2001 From: mDuo13 Date: Fri, 5 Dec 2025 16:57:17 -0800 Subject: [PATCH 12/12] Add unused diagram showing how to get escrow sequence --- docs/img/_sources/get-escrow-sequence.uxf | 262 ++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 docs/img/_sources/get-escrow-sequence.uxf diff --git a/docs/img/_sources/get-escrow-sequence.uxf b/docs/img/_sources/get-escrow-sequence.uxf new file mode 100644 index 00000000000..0eb994d47d0 --- /dev/null +++ b/docs/img/_sources/get-escrow-sequence.uxf @@ -0,0 +1,262 @@ + + + 10 + + UMLObject + + 90 + 100 + 160 + 80 + + Escrow ledger entry +-- +PreviousTxnID field +... + + + + Relation + + 240 + 130 + 210 + 50 + + lt=<. +look up transaction +by Previous TxnID + 190.0;20.0;10.0;20.0 + + + UMLObject + + 430 + 100 + 160 + 140 + + Transaction +-- +tx_json + TransactionType + Sequence + ... +meta + AffectedNodes + ... +halign=left + + + + UMLSpecialState + + 490 + 320 + 40 + 40 + + type=decision + + + + Relation + + 500 + 230 + 270 + 110 + + lt=<- +Is TransactionType == EscrowCreate? + 10.0;90.0;10.0;10.0 + + + Relation + + 520 + 320 + 120 + 40 + + lt=<- +Yes + 100.0;20.0;10.0;20.0 + + + UMLSpecialState + + 680 + 680 + 20 + 20 + + type=final + + + + + Relation + + 370 + 320 + 140 + 40 + + lt=<- +No + 10.0;20.0;120.0;20.0 + + + UMLState + + 230 + 290 + 150 + 100 + + Check metadata for the same Escrow. + +Get the prior PreviousTxnID. +style=wordwrap + + + + Relation + + 260 + 140 + 30 + 170 + + lt=.> + 10.0;150.0;10.0;10.0 + + + UMLState + + 500 + 540 + 170 + 80 + + Use this transaction's Sequence number to finish or cancel the escrow. +style=wordwrap + + + + Relation + + 570 + 610 + 130 + 100 + + lt=<- + 110.0;80.0;10.0;80.0;10.0;10.0 + + + Relation + + 150 + 50 + 30 + 70 + + lt=<- + 10.0;50.0;10.0;10.0 + + + UMLSpecialState + + 150 + 40 + 20 + 20 + + type=initial + + + + UMLSpecialState + + 670 + 440 + 40 + 40 + + type=decision + + + + UMLState + + 620 + 300 + 150 + 90 + + This is the transaction that created the escrow. +style=wordwrap + + + + Relation + + 680 + 380 + 140 + 80 + + lt=<- +Is Sequence == 0? + 10.0;60.0;10.0;10.0 + + + Relation + + 570 + 440 + 120 + 120 + + lt=<- +r2=No + 10.0;100.0;10.0;20.0;100.0;20.0 + + + UMLState + + 710 + 540 + 170 + 80 + + Use this transaction's TicketSequence number to finish or cancel the escrow. +style=wordwrap + + + + Relation + + 700 + 440 + 120 + 120 + + lt=<- +r2=Yes + 100.0;100.0;100.0;20.0;10.0;20.0 + + + Relation + + 690 + 610 + 130 + 100 + + lt=<- + 10.0;80.0;110.0;80.0;110.0;10.0 + +