mirror of
				https://github.com/XRPLF/xrpl-dev-portal.git
				synced 2025-11-04 11:55:50 +00:00 
			
		
		
		
	Compare commits
	
		
			4 Commits
		
	
	
		
			4319594cf1
			...
			timed_escr
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					3a548852ae | ||
| 
						 | 
					892f9202c5 | ||
| 
						 | 
					8c25a5ea33 | ||
| 
						 | 
					242a6cc31b | 
@@ -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()
 | 
			
		||||
@@ -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"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										156
									
								
								_code-samples/escrow/js/send-timed-escrow.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								_code-samples/escrow/js/send-timed-escrow.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,156 @@
 | 
			
		||||
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()
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
 | 
			
		||||
  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_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())
 | 
			
		||||
 | 
			
		||||
  // Construct the EscrowCreate transaction
 | 
			
		||||
  const escrowCreate = {
 | 
			
		||||
    TransactionType: 'EscrowCreate',
 | 
			
		||||
    Account: wallet.address,
 | 
			
		||||
    Destination: dest_address,
 | 
			
		||||
    Amount: amount,
 | 
			
		||||
    FinishAfter: finishAfterRippleTime
 | 
			
		||||
  }
 | 
			
		||||
  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}.`)
 | 
			
		||||
  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)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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
 | 
			
		||||
 *   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
 | 
			
		||||
  })
 | 
			
		||||
  
 | 
			
		||||
  // Display the transaction results
 | 
			
		||||
  console.log(JSON.stringify(response2.result, null, 2))
 | 
			
		||||
  if (response2.result.meta.TransactionResult === 'tesSUCCESS') {
 | 
			
		||||
    console.log('Escrow finished successfully.')
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -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 %}
 | 
			
		||||
 | 
			
		||||
<!-- TODO: re-add a working JS example. Removed the old one due to the sample code being refactored -->
 | 
			
		||||
 | 
			
		||||
## 4. Wait for validation
 | 
			
		||||
 | 
			
		||||
{% raw-partial file="/docs/_snippets/wait-for-validation.md" /%} 
 | 
			
		||||
 
 | 
			
		||||
@@ -1,208 +0,0 @@
 | 
			
		||||
---
 | 
			
		||||
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.
 | 
			
		||||
labels:
 | 
			
		||||
  - Escrow
 | 
			
		||||
  - Smart Contracts
 | 
			
		||||
---
 | 
			
		||||
# Send a Time-Held Escrow
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
{% 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" /%}
 | 
			
		||||
{% /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 %}
 | 
			
		||||
 | 
			
		||||
{% tab label="Websocket" %}
 | 
			
		||||
{% code-snippet file="/_api-examples/escrow/websocket/ledger-request.json" language="json" /%}
 | 
			
		||||
{% /tab %}
 | 
			
		||||
 | 
			
		||||
{% /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" /%} 
 | 
			
		||||
 | 
			
		||||
{% tabs %}
 | 
			
		||||
 | 
			
		||||
{% tab label="Websocket" %}
 | 
			
		||||
Request:
 | 
			
		||||
{% code-snippet file="/_api-examples/escrow/websocket/submit-request-escrowfinish-time.json" language="json" /%}
 | 
			
		||||
 | 
			
		||||
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 %}
 | 
			
		||||
 | 
			
		||||
{% 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:
 | 
			
		||||
 | 
			
		||||
{% 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 %}
 | 
			
		||||
 | 
			
		||||
{% /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)
 | 
			
		||||
- **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" /%}
 | 
			
		||||
@@ -0,0 +1,131 @@
 | 
			
		||||
---
 | 
			
		||||
seo:
 | 
			
		||||
    description: Send an escrow whose only condition for release is that a specific time has passed.
 | 
			
		||||
labels:
 | 
			
		||||
  - 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 define main function
 | 
			
		||||
 | 
			
		||||
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" %}
 | 
			
		||||
{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" before="/* send_timed_escrow" /%}
 | 
			
		||||
{% /tab %}
 | 
			
		||||
{% /tabs %}
 | 
			
		||||
 | 
			
		||||
### 3. Create the escrow
 | 
			
		||||
 | 
			
		||||
Next, the `send_timed_escrow(...)` function implements the following:
 | 
			
		||||
 | 
			
		||||
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" %}
 | 
			
		||||
{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="/* send_timed_escrow" before="/* wait_for_escrow" /%}
 | 
			
		||||
{% /tab %}
 | 
			
		||||
{% /tabs %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 4. 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.
 | 
			
		||||
 | 
			
		||||
{% tabs %}
 | 
			
		||||
{% tab label="JavaScript" %}
 | 
			
		||||
{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="/* wait_for_escrow" before="/* Sleep function" /%}
 | 
			
		||||
 | 
			
		||||
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="/* finish_escrow" /%}
 | 
			
		||||
 | 
			
		||||
{% /tab %}
 | 
			
		||||
{% /tabs %}
 | 
			
		||||
 | 
			
		||||
### 5. Finish the escrow
 | 
			
		||||
 | 
			
		||||
The `finish_escrow(...)` function implements the following:
 | 
			
		||||
 | 
			
		||||
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="JavaScript" %}
 | 
			
		||||
{% code-snippet file="/_code-samples/escrow/js/send-timed-escrow.js" language="js" from="/* finish_escrow" /%}
 | 
			
		||||
{% /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)
 | 
			
		||||
- **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" /%}
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user