Escrow Tutorials

The XRP Ledger supports held payments, or escrows, that can be executed only after a certain time has passed or a cryptographic condition has been fulfilled. Escrows can only send XRP, not issued currencies. You can use these features to build publicly-provable smart contracts. This article explains basic tasks relating to held payments.

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:

// 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

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!

2. Submit EscrowCreate transaction

Sign and submit 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.

Caution: Never submit a secret key to a server you do not control. Do not send a secret key unencrypted over the network.

Request:

{
  "id": 2,
  "command": "submit",
  "secret": "s████████████████████████████",
  "tx_json": {
      "Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
      "TransactionType": "EscrowCreate",
      "Amount": "10000",
      "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
      "FinishAfter": 557020800
  }
}

Response:

{
  "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"
    }
  }
}

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

On the live network or the Ripple Test Net, you can wait 4-7 seconds for the ledger to close automatically.

If you're running rippled in stand-alone mode, use the ledger_accept command to manually close the ledger.

4. Confirm that the escrow was created

Use the tx command 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.

Request:

{
  "id": 3,
  "command": "tx",
  "transaction": "55B2057332F8999208C43BA1E7091B423A16E5ED2736C06300B4076085205263"
}

Response:

{
  "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
  }
}

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 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 command:

Request:

{
  "id": 4,
  "command": "ledger",
  "ledger_index": "validated"
}

Response:

{
  "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
  }
}

6. Submit EscrowFinish transaction

Sign and submit 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.

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.

If the escrow has expired, you can only cancel the escrow instead.

Caution: Never submit a secret key to a server you do not control. Do not send a secret key unencrypted over the network.

Request:

{
  "id": 5,
  "command": "submit",
  "secret": "s████████████████████████████",
  "tx_json": {
    "Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
    "TransactionType": "EscrowFinish",
    "Owner": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
    "OfferSequence": 1
  }
}

Response:

{
  "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"
    }
  }
}

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

On the live network or the Ripple Test Net, you can wait 4-7 seconds for the ledger to close automatically.

If you're running rippled in stand-alone mode, use the ledger_accept command to manually close the ledger.

8. Confirm final result

Use the tx command 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:

{
  "id": 21,
  "command": "tx",
  "transaction": "41856A742B3CAF307E7B4D0B850F302101F0F415B785454F7501E9960A2A1F6B"
}

Response:

{
  "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
  }
}

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. For fulfillments, Ripple recommends using one of the following methods to generate the fulfillment:

  • Use a cryptographically secure source of randomness to generate at least 32 random bytes.
  • Follow Interledger Protocol's PSK specification and use an HMAC-SHA-256 of the ILP packet as the fulfillment.

Example JavaScript code for a random fulfillment and condition:

cc = require('five-bells-condition');

const fulfillment_bytes = crypto.randomBytes(32);
const myFulfillment = new cc.PreimageSha256();
myFulfillment.setPreimage(fulfillment_bytes);
console.log(myFulfillment.serializeBinary().toString('hex'));
// (Random hexadecimal, 72 chars in length)
console.log(myFulfillment.getConditionBinary().toString('hex'));
// (Random hexadecimal, 78 chars in length)

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:

const rippleOffset = 946684800;
const CancelAfter = Math.floor(Date.now() / 1000) + (24*60*60) - rippleOffset;
console.log(CancelAfter);
// Example: 556927412

Warning: In the XRP Ledger, you must specify time as seconds since the Ripple Epoch (2000-01-01T00:00:00Z). If you use a UNIX time in the CancelAfter or FinishAfter field without converting to the equivalent Ripple time first, that sets the unlock time to an extra 30 years in the future!

3. Submit EscrowCreate transaction

Sign and submit 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.

Caution: Never submit a secret key to a server you do not control. Do not send a secret key unencrypted over the network.

Request:

{
  "id": 1,
  "command": "submit",
  "secret": "s████████████████████████████",
  "tx_json": {
    "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
    "TransactionType": "EscrowCreate",
    "Amount": "100000",
    "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
    "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
    "CancelAfter": 556927412
  }
}

Response:

{
  "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"
    }
  }
}

4. Wait for validation

On the live network or the Ripple Test Net, you can wait 4-7 seconds for the ledger to close automatically.

If you're running rippled in stand-alone mode, use the ledger_accept command to manually close the ledger.

5. Confirm that the escrow was created

Use the tx command 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.

Request:

{
  "id": 3,
  "command": "tx",
  "transaction": "E22D1F6EB006CAD35E0DBD3B4F3748427055E4C143EBE95AA6603823AEEAD324"
}

Response:

{
  "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
  }
}

6. Submit EscrowFinish transaction

Sign and submit 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) 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.

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 tecNO_PERMISSION if the previously-closed ledger's close time is before the FinishAfter time.

If the escrow has expired, you can only cancel the escrow instead.

Caution: Never submit a secret key to a server you do not control. Do not send a secret key unencrypted over the network.

{
  "id": 4,
  "command": "submit",
  "secret": "s████████████████████████████",
  "tx_json": {
    "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
    "TransactionType": "EscrowFinish",
    "Owner": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
    "OfferSequence": 5,
    "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
    "Fulfillment": "A0228020D280D1A02BAD0D2EBC0528B92E9BF37AC3E2530832C2C52620307135156F1048",
    "Fee": "500"
  }
}

Response:

{
  "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"
    }
  }
}

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

On the live network or the Ripple Test Net, you can wait 4-7 seconds for the ledger to close automatically.

If you're running rippled in stand-alone mode, use the ledger_accept command to manually close the ledger.

8. Confirm final result

Use the tx command 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:

{
  "id": 20,
  "command": "tx",
  "transaction": "52C4F626FE6F33699B6BE8ADF362836DDCE9B0B1294BFAA15D65D61501350BE6"
}

Response:

{
  "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": "52C4F626FE6F33699B6BE8ADF362836DDCE9B0B1294BFAA15D65D61501350BE6",
    "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
  }
}

Cancel an expired escrow

1. Confirm the expired escrow

An escrow in the XRP Ledger is expired when its CancelAfter time is lower than the close_time of a validated ledger version. (If the escrow does not have a CancelAfter time, it never expires.) You can look up the close time of the latest validated ledger with the ledger command:

Request:

{
  "id": 4,
  "command": "ledger",
  "ledger_index": "validated"
}

Response:

{
  "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
  }
}

You can look up the escrow and compare to the CancelAfter time using the account_objects command:

Request:

{
  "id": 2,
  "command": "account_objects",
  "account": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT",
  "ledger": "validated",
  "type": "escrow"
}

Response:

{
  "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
  }
}

2. Submit EscrowCancel transaction

Anyone can cancel an expired escrow in the XRP Ledger by signing and submitting 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.

Caution: Never submit a secret key to a server you do not control. Do not send a secret key unencrypted over the network.

Request:

{
  "id": 5,
  "command": "submit",
  "secret": "s████████████████████████████",
  "tx_json": {
    "Account": "rhgdnc82FwHFUKXp9ZcpgwXWRAxKf5Buqp",
    "TransactionType": "EscrowCancel",
    "Owner": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT",
    "OfferSequence": 1
  }
}

Response:

{
  "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"
    }
  }
}

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

On the live network or the Ripple Test Net, you can wait 4-7 seconds for the ledger to close automatically.

If you're running rippled in stand-alone mode, use the ledger_accept command to manually close the ledger.

4. Confirm final result

Use the tx command 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.

Request:

{
  "id": 6,
  "command": "tx",
  "transaction": "65F36C5514153D94F0ADE5CE747061A5E70B73B56B4C66DA5040D99CAF252831"
}

Response:

{
  "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
  }
}

In the above example, r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT is the sender of the escrow, and the increase in Balance from 9999989990 drops to 9999999990 drops represents the return of the escrowed 10,000 drops of XRP (0.01 XRP).

Look up escrows by sender

All pending escrows are stored in the ledger as Escrow objects. You can look up escrow nodes owned by your address using the account_objects method.

Request:

{
  "id": 5,
  "command": "account_objects",
  "account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
  "ledger_index": "validated",
  "type": "escrow"
}

Response:

{
  "id": 5,
  "status": "success",
  "type": "response",
  "result": {
    "account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
    "account_objects": [
      {
        "Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
        "Amount": "100000",
        "CancelAfter": 556927412,
        "Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
        "Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
        "Flags": 0,
        "LedgerEntryType": "Escrow",
        "OwnerNode": "0000000000000000",
        "PreviousTxnID": "E22D1F6EB006CAD35E0DBD3B4F3748427055E4C143EBE95AA6603823AEEAD324",
        "PreviousTxnLgrSeq": 1772019,
        "index": "E2CF730A31FD419382350C9DBD8DB7CD775BA5AA9B97A9BE9AB07304AA217A75"
      }
    ],
    "ledger_hash": "F2ABEA175F4AB871845B01CB51E4324DBA2C2553EC34448D4AB1EB0A3F2D8EFB",
    "ledger_index": 1772020,
    "validated": true
  }
}

Tip: If you don't know what OfferSequence to use in the EscrowFinish transaction to execute an escrow, use the tx method to look up the transaction that created the escrow, using the identifying hash of the transaction in the Escrow's PreviousTxnID field. Use the Sequence value of that transaction as the OfferSequence value when finishing the escrow.