Files
xrpl-dev-portal/_code-samples/escrow/js/sendFungibleTokenEscrow.js
2026-04-09 11:16:47 -07:00

359 lines
12 KiB
JavaScript

// This example demonstrates how to create escrows that hold fungible tokens.
// It covers MPTs and Trust Line Tokens, and uses conditional and timed escrows.
import xrpl from 'xrpl'
import { PreimageSha256 } from 'five-bells-condition'
import { randomBytes } from 'crypto'
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233')
await client.connect()
// Fund an issuer account and an escrow creator account ----------------------
console.log(`\n=== Funding Accounts ===\n`)
const [
{ wallet: issuer },
{ wallet: creator }
] = await Promise.all([
client.fundWallet(),
client.fundWallet()
])
console.log(`Issuer: ${issuer.address}`)
console.log(`Escrow Creator: ${creator.address}`)
// ====== Conditional MPT Escrow ======
// Issuer creates an MPT ----------------------
console.log('\n=== Creating MPT ===\n')
const mptCreateTx = {
TransactionType: 'MPTokenIssuanceCreate',
Account: issuer.address,
MaximumAmount: '1000000',
Flags: xrpl.MPTokenIssuanceCreateFlags.tfMPTCanEscrow
}
// Validate the transaction structure before submitting
xrpl.validate(mptCreateTx)
console.log(JSON.stringify(mptCreateTx, null, 2))
// Submit, sign, and wait for validation
console.log(`\nSubmitting MPTokenIssuanceCreate transaction...`)
const mptCreateResponse = await client.submitAndWait(mptCreateTx, {
wallet: issuer,
autofill: true
})
if (mptCreateResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
console.error(`MPTokenIssuanceCreate failed: ${mptCreateResponse.result.meta.TransactionResult}`)
await client.disconnect()
process.exit(1)
}
// Extract the MPT issuance ID from the transaction result
const mptIssuanceId = mptCreateResponse.result.meta.mpt_issuance_id
console.log(`MPT created: ${mptIssuanceId}`)
// Escrow Creator authorizes the MPT ----------------------
console.log('\n=== Escrow Creator Authorizing MPT ===\n')
const mptAuthTx = {
TransactionType: 'MPTokenAuthorize',
Account: creator.address,
MPTokenIssuanceID: mptIssuanceId
}
xrpl.validate(mptAuthTx)
console.log(JSON.stringify(mptAuthTx, null, 2))
console.log(`\nSubmitting MPTokenAuthorize transaction...`)
const mptAuthResponse = await client.submitAndWait(mptAuthTx, {
wallet: creator,
autofill: true
})
if (mptAuthResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
console.error(`MPTokenAuthorize failed: ${mptAuthResponse.result.meta.TransactionResult}`)
await client.disconnect()
process.exit(1)
}
console.log('Escrow Creator authorized for MPT.')
// Issuer sends MPTs to escrow creator ----------------------
console.log('\n=== Issuer Sending MPTs to Escrow Creator ===\n')
const mptPaymentTx = {
TransactionType: 'Payment',
Account: issuer.address,
Destination: creator.address,
Amount: {
mpt_issuance_id: mptIssuanceId,
value: '5000'
}
}
xrpl.validate(mptPaymentTx)
console.log(JSON.stringify(mptPaymentTx, null, 2))
console.log(`\nSubmitting MPT Payment transaction...`)
const mptPaymentResponse = await client.submitAndWait(mptPaymentTx, {
wallet: issuer,
autofill: true
})
if (mptPaymentResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
console.error(`MPT Payment failed: ${mptPaymentResponse.result.meta.TransactionResult}`)
await client.disconnect()
process.exit(1)
}
console.log('Successfully sent 5000 MPTs to Escrow Creator.')
// Escrow Creator creates a conditional MPT escrow ----------------------
console.log('\n=== Creating Conditional MPT Escrow ===\n')
// Generate crypto-condition
const preimage = randomBytes(32)
const fulfillment = new PreimageSha256()
fulfillment.setPreimage(preimage)
const fulfillmentHex = fulfillment.serializeBinary().toString('hex').toUpperCase()
const conditionHex = fulfillment.getConditionBinary().toString('hex').toUpperCase()
console.log(`Condition: ${conditionHex}`)
console.log(`Fulfillment: ${fulfillmentHex}\n`)
// Set expiration (300 seconds from now)
const cancelAfter = new Date()
cancelAfter.setSeconds(cancelAfter.getSeconds() + 300)
const cancelAfterRippleTime = xrpl.isoTimeToRippleTime(cancelAfter.toISOString())
const mptEscrowCreateTx = {
TransactionType: 'EscrowCreate',
Account: creator.address,
Destination: issuer.address,
Amount: {
mpt_issuance_id: mptIssuanceId,
value: '1000'
},
Condition: conditionHex,
CancelAfter: cancelAfterRippleTime // Fungible token escrows require a CancelAfter time
}
xrpl.validate(mptEscrowCreateTx)
console.log(JSON.stringify(mptEscrowCreateTx, null, 2))
console.log(`\nSubmitting MPT EscrowCreate transaction...`)
const mptEscrowResponse = await client.submitAndWait(mptEscrowCreateTx, {
wallet: creator,
autofill: true
})
if (mptEscrowResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
console.error(`MPT EscrowCreate failed: ${mptEscrowResponse.result.meta.TransactionResult}`)
await client.disconnect()
process.exit(1)
}
// Save the sequence number to identify the escrow later
const mptEscrowSeq = mptEscrowResponse.result.tx_json.Sequence
console.log(`Conditional MPT escrow created. Sequence: ${mptEscrowSeq}`)
// Finish the conditional MPT escrow with the fulfillment ----------------------
console.log('\n=== Finishing Conditional MPT Escrow ===\n')
const mptEscrowFinishTx = {
TransactionType: 'EscrowFinish',
Account: creator.address,
Owner: creator.address,
OfferSequence: mptEscrowSeq,
Condition: conditionHex,
Fulfillment: fulfillmentHex
}
xrpl.validate(mptEscrowFinishTx)
console.log(JSON.stringify(mptEscrowFinishTx, null, 2))
console.log(`\nSubmitting EscrowFinish transaction...`)
const mptFinishResponse = await client.submitAndWait(mptEscrowFinishTx, {
wallet: creator,
autofill: true
})
if (mptFinishResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
console.error(`MPT EscrowFinish failed: ${mptFinishResponse.result.meta.TransactionResult}`)
await client.disconnect()
process.exit(1)
}
console.log(`Conditional MPT escrow finished successfully: https://testnet.xrpl.org/transactions/${mptFinishResponse.result.hash}`)
// ====== Timed Trust Line Token Escrow ======
// Enable trust line token escrows on the issuer ----------------------
console.log('\n=== Enabling Trust Line Token Escrows on Issuer ===\n')
const accountSetTx = {
TransactionType: 'AccountSet',
Account: issuer.address,
SetFlag: xrpl.AccountSetAsfFlags.asfAllowTrustLineLocking
}
xrpl.validate(accountSetTx)
console.log(JSON.stringify(accountSetTx, null, 2))
console.log(`\nSubmitting AccountSet transaction...`)
const accountSetResponse = await client.submitAndWait(accountSetTx, {
wallet: issuer,
autofill: true
})
if (accountSetResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
console.error(`AccountSet failed: ${accountSetResponse.result.meta.TransactionResult}`)
await client.disconnect()
process.exit(1)
}
console.log('Trust line token escrows enabled by issuer.')
// Escrow Creator sets up a trust line to the issuer ----------------------
console.log('\n=== Setting Up Trust Line ===\n')
const currencyCode = 'IOU'
const trustSetTx = {
TransactionType: 'TrustSet',
Account: creator.address,
LimitAmount: {
currency: currencyCode,
issuer: issuer.address,
value: '10000000'
}
}
xrpl.validate(trustSetTx)
console.log(JSON.stringify(trustSetTx, null, 2))
console.log(`\nSubmitting TrustSet transaction...`)
const trustResponse = await client.submitAndWait(trustSetTx, {
wallet: creator,
autofill: true
})
if (trustResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
console.error(`TrustSet failed: ${trustResponse.result.meta.TransactionResult}`)
await client.disconnect()
process.exit(1)
}
console.log(`Trust line successfully created for "${currencyCode}" tokens.`)
// Issuer sends IOU tokens to creator ----------------------
console.log('\n=== Issuer Sending IOU Tokens to Escrow Creator ===\n')
const iouPaymentTx = {
TransactionType: 'Payment',
Account: issuer.address,
Destination: creator.address,
Amount: {
currency: currencyCode,
value: '5000',
issuer: issuer.address
}
}
xrpl.validate(iouPaymentTx)
console.log(JSON.stringify(iouPaymentTx, null, 2))
console.log(`\nSubmitting Trust Line Token payment transaction...`)
const iouPayResponse = await client.submitAndWait(iouPaymentTx, {
wallet: issuer,
autofill: true
})
if (iouPayResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
console.error(`Trust Line Token payment failed: ${iouPayResponse.result.meta.TransactionResult}`)
await client.disconnect()
process.exit(1)
}
console.log(`Successfully sent 5000 ${currencyCode} tokens.`)
// Escrow Creator creates a timed trust line token escrow ----------------------
console.log('\n=== Creating Timed Trust Line Token Escrow ===\n')
const delay = 10 // seconds
const now = new Date()
const finishAfter = new Date(now.getTime() + delay * 1000)
const finishAfterRippleTime = xrpl.isoTimeToRippleTime(finishAfter.toISOString())
console.log(`Escrow will mature after: ${finishAfter.toLocaleString()}\n`)
const iouCancelAfter = new Date(now.getTime() + 300 * 1000)
const iouCancelAfterRippleTime = xrpl.isoTimeToRippleTime(iouCancelAfter.toISOString())
const iouEscrowCreateTx = {
TransactionType: 'EscrowCreate',
Account: creator.address,
Destination: issuer.address,
Amount: {
currency: currencyCode,
value: '1000',
issuer: issuer.address
},
FinishAfter: finishAfterRippleTime,
CancelAfter: iouCancelAfterRippleTime
}
xrpl.validate(iouEscrowCreateTx)
console.log(JSON.stringify(iouEscrowCreateTx, null, 2))
console.log(`\nSubmitting Trust Line Token EscrowCreate transaction...`)
const iouEscrowResponse = await client.submitAndWait(iouEscrowCreateTx, {
wallet: creator,
autofill: true
})
if (iouEscrowResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
console.error(`Trust Line Token EscrowCreate failed: ${iouEscrowResponse.result.meta.TransactionResult}`)
await client.disconnect()
process.exit(1)
}
// Save the sequence number to identify the escrow later
const iouEscrowSeq = iouEscrowResponse.result.tx_json.Sequence
console.log(`Trust Line Token escrow created. Sequence: ${iouEscrowSeq}`)
// Wait for the escrow to mature, then finish it --------------------
console.log(`\n=== Waiting For Timed Trust Line Token Escrow to Mature ===\n`)
// Sleep function to countdown delay until escrow matures
function sleep (delayInSeconds) {
return new Promise((resolve) => setTimeout(resolve, delayInSeconds * 1000))
}
for (let i = delay; i >= 0; i--) {
process.stdout.write(`\rWaiting for escrow to mature... ${i}s remaining...`)
await sleep(1)
}
console.log('\rWaiting for escrow to mature... done. ')
// Confirm latest validated ledger close time is after the FinishAfter time
let escrowReady = false
while (!escrowReady) {
const validatedLedger = await client.request({
command: 'ledger',
ledger_index: 'validated'
})
const ledgerCloseTime = validatedLedger.result.ledger.close_time
console.log(`Latest validated ledger closed at: ${new Date(xrpl.rippleTimeToISOTime(ledgerCloseTime)).toLocaleString()}`)
if (ledgerCloseTime > finishAfterRippleTime) {
escrowReady = true
console.log('Escrow confirmed ready to finish.')
} else {
let timeDifference = finishAfterRippleTime - ledgerCloseTime
if (timeDifference === 0) { timeDifference = 1 }
console.log(`Escrow needs to wait another ${timeDifference}s.`)
await sleep(timeDifference)
}
}
// Finish the timed trust line token escrow --------------------
console.log('\n=== Finishing Timed Trust Line Token Escrow ===\n')
const iouEscrowFinishTx = {
TransactionType: 'EscrowFinish',
Account: creator.address,
Owner: creator.address,
OfferSequence: iouEscrowSeq
}
xrpl.validate(iouEscrowFinishTx)
console.log(JSON.stringify(iouEscrowFinishTx, null, 2))
console.log(`\nSubmitting EscrowFinish transaction...`)
const iouFinishResponse = await client.submitAndWait(iouEscrowFinishTx, {
wallet: creator,
autofill: true
})
if (iouFinishResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
console.error(`Trust Line Token EscrowFinish failed: ${iouFinishResponse.result.meta.TransactionResult}`)
await client.disconnect()
process.exit(1)
}
console.log(`Timed Trust Line Token escrow finished successfully: https://testnet.xrpl.org/transactions/${iouFinishResponse.result.hash}`)
await client.disconnect()