mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-20 12:15:51 +00:00
fix: escrow finish and cancel integration (#2382)
* fix: escrow finish and cancel integration * add docs
This commit is contained in:
@@ -8,7 +8,7 @@ import {
|
|||||||
type XrplIntegrationTestContext,
|
type XrplIntegrationTestContext,
|
||||||
} from '../setup'
|
} from '../setup'
|
||||||
import {
|
import {
|
||||||
calculateWaitTimeForTransaction,
|
waitForAndForceProgressLedgerTime,
|
||||||
generateFundedWallet,
|
generateFundedWallet,
|
||||||
getXRPBalance,
|
getXRPBalance,
|
||||||
testTransaction,
|
testTransaction,
|
||||||
@@ -40,8 +40,6 @@ describe('EscrowCancel', function () {
|
|||||||
})
|
})
|
||||||
).result.ledger.close_time
|
).result.ledger.close_time
|
||||||
|
|
||||||
const waitTimeInMs = calculateWaitTimeForTransaction(CLOSE_TIME)
|
|
||||||
|
|
||||||
const createTx: EscrowCreate = {
|
const createTx: EscrowCreate = {
|
||||||
Account: testContext.wallet.classicAddress,
|
Account: testContext.wallet.classicAddress,
|
||||||
TransactionType: 'EscrowCreate',
|
TransactionType: 'EscrowCreate',
|
||||||
@@ -86,14 +84,10 @@ describe('EscrowCancel', function () {
|
|||||||
OfferSequence: sequence,
|
OfferSequence: sequence,
|
||||||
}
|
}
|
||||||
|
|
||||||
// We set the CancelAfter timer to be 3 seconds after the last ledger close_time. We need to wait this long
|
await waitForAndForceProgressLedgerTime(
|
||||||
// before we can cancel the escrow.
|
testContext.client,
|
||||||
const cancelAfterTimerPromise = new Promise((resolve) => {
|
CLOSE_TIME + 3,
|
||||||
setTimeout(resolve, waitTimeInMs)
|
)
|
||||||
})
|
|
||||||
|
|
||||||
// Make sure we wait long enough before canceling the escrow.
|
|
||||||
await cancelAfterTimerPromise
|
|
||||||
|
|
||||||
// rippled uses the close time of the previous ledger
|
// rippled uses the close time of the previous ledger
|
||||||
await sendLedgerAccept(testContext.client)
|
await sendLedgerAccept(testContext.client)
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ import {
|
|||||||
type XrplIntegrationTestContext,
|
type XrplIntegrationTestContext,
|
||||||
} from '../setup'
|
} from '../setup'
|
||||||
import {
|
import {
|
||||||
calculateWaitTimeForTransaction,
|
|
||||||
generateFundedWallet,
|
generateFundedWallet,
|
||||||
getXRPBalance,
|
getXRPBalance,
|
||||||
sendLedgerAccept,
|
sendLedgerAccept,
|
||||||
testTransaction,
|
testTransaction,
|
||||||
|
getLedgerCloseTime,
|
||||||
|
waitForAndForceProgressLedgerTime,
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
|
|
||||||
// how long before each test case times out
|
// how long before each test case times out
|
||||||
@@ -32,14 +33,7 @@ describe('EscrowFinish', function () {
|
|||||||
const wallet1 = await generateFundedWallet(testContext.client)
|
const wallet1 = await generateFundedWallet(testContext.client)
|
||||||
|
|
||||||
// get the most recent close_time from the standalone container for cancel & finish after.
|
// get the most recent close_time from the standalone container for cancel & finish after.
|
||||||
const CLOSE_TIME: number = (
|
const CLOSE_TIME = await getLedgerCloseTime(testContext.client)
|
||||||
await testContext.client.request({
|
|
||||||
command: 'ledger',
|
|
||||||
ledger_index: 'validated',
|
|
||||||
})
|
|
||||||
).result.ledger.close_time
|
|
||||||
|
|
||||||
const waitTimeInMs = calculateWaitTimeForTransaction(CLOSE_TIME)
|
|
||||||
|
|
||||||
const AMOUNT = 10000
|
const AMOUNT = 10000
|
||||||
|
|
||||||
@@ -51,10 +45,6 @@ describe('EscrowFinish', function () {
|
|||||||
FinishAfter: CLOSE_TIME + 2,
|
FinishAfter: CLOSE_TIME + 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
const finishAfterPromise = new Promise((resolve) => {
|
|
||||||
setTimeout(resolve, waitTimeInMs)
|
|
||||||
})
|
|
||||||
|
|
||||||
await testTransaction(testContext.client, createTx, testContext.wallet)
|
await testTransaction(testContext.client, createTx, testContext.wallet)
|
||||||
|
|
||||||
const initialBalance = await getXRPBalance(testContext.client, wallet1)
|
const initialBalance = await getXRPBalance(testContext.client, wallet1)
|
||||||
@@ -83,7 +73,11 @@ describe('EscrowFinish', function () {
|
|||||||
OfferSequence: sequence!,
|
OfferSequence: sequence!,
|
||||||
}
|
}
|
||||||
|
|
||||||
await finishAfterPromise
|
// wait for the escrow to be ready to finish
|
||||||
|
await waitForAndForceProgressLedgerTime(
|
||||||
|
testContext.client,
|
||||||
|
CLOSE_TIME + 2,
|
||||||
|
)
|
||||||
|
|
||||||
// rippled uses the close time of the previous ledger
|
// rippled uses the close time of the previous ledger
|
||||||
await sendLedgerAccept(testContext.client)
|
await sendLedgerAccept(testContext.client)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
type SubmitResponse,
|
type SubmitResponse,
|
||||||
TimeoutError,
|
TimeoutError,
|
||||||
NotConnectedError,
|
NotConnectedError,
|
||||||
unixTimeToRippleTime,
|
|
||||||
} from '../../src'
|
} from '../../src'
|
||||||
import { Payment, Transaction } from '../../src/models/transactions'
|
import { Payment, Transaction } from '../../src/models/transactions'
|
||||||
import { hashSignedTx } from '../../src/utils/hashes'
|
import { hashSignedTx } from '../../src/utils/hashes'
|
||||||
@@ -76,34 +75,6 @@ export async function ledgerAccept(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempt to get the time after which we can check for the escrow to be finished.
|
|
||||||
* Sometimes the ledger close_time is in the future, so we need to wait for it to catch up.
|
|
||||||
*
|
|
||||||
* @param targetTime - The target wait time, before accounting for current ledger time.
|
|
||||||
* @param minimumWaitTimeMs - The minimum wait time in milliseconds.
|
|
||||||
* @param maximumWaitTimeMs - The maximum wait time in milliseconds.
|
|
||||||
* @returns The wait time in milliseconds.
|
|
||||||
*/
|
|
||||||
export function calculateWaitTimeForTransaction(
|
|
||||||
targetTime: number,
|
|
||||||
minimumWaitTimeMs = 5000,
|
|
||||||
maximumWaitTimeMs = 20000,
|
|
||||||
): number {
|
|
||||||
const currentTimeUnixMs = Math.floor(new Date().getTime())
|
|
||||||
const currentTimeRippleSeconds = unixTimeToRippleTime(currentTimeUnixMs)
|
|
||||||
const closeTimeCurrentTimeDiffSeconds = currentTimeRippleSeconds - targetTime
|
|
||||||
const closeTimeCurrentTimeDiffMs = closeTimeCurrentTimeDiffSeconds * 1000
|
|
||||||
return Math.max(
|
|
||||||
minimumWaitTimeMs,
|
|
||||||
Math.min(
|
|
||||||
Math.abs(closeTimeCurrentTimeDiffMs) + minimumWaitTimeMs,
|
|
||||||
// Maximum wait time of 20 seconds
|
|
||||||
maximumWaitTimeMs,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function subscribeDone(client: Client): void {
|
export function subscribeDone(client: Client): void {
|
||||||
client.removeAllListeners()
|
client.removeAllListeners()
|
||||||
}
|
}
|
||||||
@@ -299,3 +270,70 @@ export async function getXRPBalance(
|
|||||||
}
|
}
|
||||||
return (await client.request(request)).result.account_data.Balance
|
return (await client.request(request)).result.account_data.Balance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the close time of the ledger.
|
||||||
|
*
|
||||||
|
* @param client - The client object.
|
||||||
|
* @returns - A promise that resolves to the close time of the ledger.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const closeTime = await getLedgerCloseTime(client);
|
||||||
|
* console.log(closeTime); // Output: 1626424978
|
||||||
|
*/
|
||||||
|
export async function getLedgerCloseTime(client: Client): Promise<number> {
|
||||||
|
const CLOSE_TIME: number = (
|
||||||
|
await client.request({
|
||||||
|
command: 'ledger',
|
||||||
|
ledger_index: 'validated',
|
||||||
|
})
|
||||||
|
).result.ledger.close_time
|
||||||
|
|
||||||
|
return CLOSE_TIME
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for the ledger time to reach a specific value and forces ledger progress if necessary.
|
||||||
|
*
|
||||||
|
* @param client - The client object.
|
||||||
|
* @param ledgerTime - The target ledger time.
|
||||||
|
* @param [retries=20] - The number of retries before throwing an error.
|
||||||
|
* @returns - A promise that resolves when the ledger time reaches the target value.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* try {
|
||||||
|
* await waitForAndForceProgressLedgerTime(client, 1626424978, 10);
|
||||||
|
* console.log('Ledger time reached.'); // Output: Ledger time reached.
|
||||||
|
* } catch (error) {
|
||||||
|
* console.error(error);
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export async function waitForAndForceProgressLedgerTime(
|
||||||
|
client: Client,
|
||||||
|
ledgerTime: number,
|
||||||
|
retries = 20,
|
||||||
|
): Promise<void> {
|
||||||
|
async function getCloseTime(): Promise<boolean> {
|
||||||
|
const CLOSE_TIME: number = await getLedgerCloseTime(client)
|
||||||
|
if (CLOSE_TIME >= ledgerTime) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let retryCounter = retries || 0
|
||||||
|
|
||||||
|
while (retryCounter > 0) {
|
||||||
|
// eslint-disable-next-line no-await-in-loop -- Necessary for retries
|
||||||
|
if (await getCloseTime()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-await-in-loop -- Necessary for retries
|
||||||
|
await ledgerAccept(client)
|
||||||
|
retryCounter -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Ledger time not reached after ${retries} retries.`)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user