fix: escrow finish and cancel integration (#2382)

* fix: escrow finish and cancel integration

* add docs
This commit is contained in:
justinr1234
2023-07-17 16:56:06 -05:00
committed by GitHub
parent d47a277e25
commit e760a4898d
3 changed files with 80 additions and 54 deletions

View File

@@ -8,7 +8,7 @@ import {
type XrplIntegrationTestContext,
} from '../setup'
import {
calculateWaitTimeForTransaction,
waitForAndForceProgressLedgerTime,
generateFundedWallet,
getXRPBalance,
testTransaction,
@@ -40,8 +40,6 @@ describe('EscrowCancel', function () {
})
).result.ledger.close_time
const waitTimeInMs = calculateWaitTimeForTransaction(CLOSE_TIME)
const createTx: EscrowCreate = {
Account: testContext.wallet.classicAddress,
TransactionType: 'EscrowCreate',
@@ -86,14 +84,10 @@ describe('EscrowCancel', function () {
OfferSequence: sequence,
}
// We set the CancelAfter timer to be 3 seconds after the last ledger close_time. We need to wait this long
// before we can cancel the escrow.
const cancelAfterTimerPromise = new Promise((resolve) => {
setTimeout(resolve, waitTimeInMs)
})
// Make sure we wait long enough before canceling the escrow.
await cancelAfterTimerPromise
await waitForAndForceProgressLedgerTime(
testContext.client,
CLOSE_TIME + 3,
)
// rippled uses the close time of the previous ledger
await sendLedgerAccept(testContext.client)

View File

@@ -8,11 +8,12 @@ import {
type XrplIntegrationTestContext,
} from '../setup'
import {
calculateWaitTimeForTransaction,
generateFundedWallet,
getXRPBalance,
sendLedgerAccept,
testTransaction,
getLedgerCloseTime,
waitForAndForceProgressLedgerTime,
} from '../utils'
// how long before each test case times out
@@ -32,14 +33,7 @@ describe('EscrowFinish', function () {
const wallet1 = await generateFundedWallet(testContext.client)
// get the most recent close_time from the standalone container for cancel & finish after.
const CLOSE_TIME: number = (
await testContext.client.request({
command: 'ledger',
ledger_index: 'validated',
})
).result.ledger.close_time
const waitTimeInMs = calculateWaitTimeForTransaction(CLOSE_TIME)
const CLOSE_TIME = await getLedgerCloseTime(testContext.client)
const AMOUNT = 10000
@@ -51,10 +45,6 @@ describe('EscrowFinish', function () {
FinishAfter: CLOSE_TIME + 2,
}
const finishAfterPromise = new Promise((resolve) => {
setTimeout(resolve, waitTimeInMs)
})
await testTransaction(testContext.client, createTx, testContext.wallet)
const initialBalance = await getXRPBalance(testContext.client, wallet1)
@@ -83,7 +73,11 @@ describe('EscrowFinish', function () {
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
await sendLedgerAccept(testContext.client)

View File

@@ -10,7 +10,6 @@ import {
type SubmitResponse,
TimeoutError,
NotConnectedError,
unixTimeToRippleTime,
} from '../../src'
import { Payment, Transaction } from '../../src/models/transactions'
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 {
client.removeAllListeners()
}
@@ -299,3 +270,70 @@ export async function getXRPBalance(
}
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.`)
}