Remove some old Escrow tutorials and rewrite conditional escrow tutorial in new format

This commit is contained in:
mDuo13
2025-12-04 15:31:57 -08:00
parent f26008d2ba
commit ebd1fd2c6a
31 changed files with 199 additions and 1321 deletions

View File

@@ -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" /%}

View File

@@ -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. <!-- SPELLING_IGNORE: psk -->
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 %}
<!-- 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" /%}
## 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" /%}

View File

@@ -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" /%}

View File

@@ -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.
Heres 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. <!--#{ once set up secure signing tutorial is available, link to it from here }# -->
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)